summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Wildemann <metalstrolch@users.noreply.github.com>2018-07-15 09:23:16 +0200
committerjkoan <jkoan@users.noreply.github.com>2018-07-15 09:23:16 +0200
commitdc8f7f1b182450ed017d0262872820cc26f08dc9 (patch)
tree2e28972c68da377f056b774a6126254a3617cb7c
parent380fe30a6b5d0c9212d84f7adb15e30ef5ca8ffa (diff)
downloadnavit-dc8f7f1b182450ed017d0262872820cc26f08dc9.tar.gz
update in-tree espeak to espeak-1.48.04 (#624)
* update in-tree espeak to espeak-1.48.04 Update the C-ified version of espeak to espeak-1.48.04. Currently builds and links for Sailfish. Untested for Windows builds yet. Throws a lot of warnings still. * Make private functions static in espeak. Convert the obviously private functions to static in order to fix warnings. There are a lot not so obvious left that are used in other files by extern statement. Silly. * correctly assign exported methods to headers This asigns exported functions to headers avoiding missing prototypes warning. * move vars out of for loop as Sailfish disallows c99 Standard build under Sailfosh disallows c99 features. Therefore move variables out of for loop declaration. * espeak prepare for Windows build * fix:speech:espeak Adapt to newer espeak version 1.48.04 * redo. removing the files from build was the false way * Disable mbrola support as this fails on windows * remove uneccesary posix calls on windows
-rw-r--r--navit/speech/espeak/speak.c6
-rw-r--r--navit/support/espeak/CMakeLists.txt9
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/compiledict.c912
-rw-r--r--navit/support/espeak/compiledict.h20
-rw-r--r--navit/support/espeak/debug.h2
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/dictionary.c2014
-rw-r--r--navit/support/espeak/dictionary.h19
-rw-r--r--navit/support/espeak/espeak-data/af_dictbin74925 -> 82372 bytes
-rw-r--r--navit/support/espeak/espeak-data/am_dictbin0 -> 3334 bytes
-rw-r--r--navit/support/espeak/espeak-data/an_dictbin0 -> 6717 bytes
-rw-r--r--navit/support/espeak/espeak-data/as_dictbin0 -> 5017 bytes
-rw-r--r--navit/support/espeak/espeak-data/az_dictbin0 -> 2135 bytes
-rw-r--r--navit/support/espeak/espeak-data/bg_dictbin0 -> 27006 bytes
-rw-r--r--navit/support/espeak/espeak-data/bn_dictbin0 -> 8087 bytes
-rw-r--r--navit/support/espeak/espeak-data/ca_dictbin4212 -> 4153 bytes
-rwxr-xr-xnavit/support/espeak/espeak-data/config9
-rw-r--r--navit/support/espeak/espeak-data/cs_dictbin7798 -> 7565 bytes
-rw-r--r--navit/support/espeak/espeak-data/cy_dictbin3541 -> 3461 bytes
-rw-r--r--navit/support/espeak/espeak-data/da_dictbin5327 -> 208489 bytes
-rw-r--r--navit/support/espeak/espeak-data/de_dictbin19322 -> 21816 bytes
-rw-r--r--navit/support/espeak/espeak-data/el_dictbin4585 -> 5351 bytes
-rw-r--r--navit/support/espeak/espeak-data/en_dictbin87069 -> 116291 bytes
-rw-r--r--navit/support/espeak/espeak-data/eo_dictbin4746 -> 4677 bytes
-rw-r--r--navit/support/espeak/espeak-data/es_dictbin5309 -> 6111 bytes
-rw-r--r--navit/support/espeak/espeak-data/et_dictbin0 -> 6767 bytes
-rw-r--r--navit/support/espeak/espeak-data/eu_dictbin0 -> 2217 bytes
-rw-r--r--navit/support/espeak/espeak-data/fa_dictbin0 -> 231857 bytes
-rw-r--r--navit/support/espeak/espeak-data/fi_dictbin4567 -> 5120 bytes
-rw-r--r--navit/support/espeak/espeak-data/fr_dictbin19363 -> 21422 bytes
-rw-r--r--navit/support/espeak/espeak-data/ga_dictbin0 -> 8917 bytes
-rw-r--r--navit/support/espeak/espeak-data/gd_dictbin0 -> 3794 bytes
-rw-r--r--navit/support/espeak/espeak-data/grc_dictbin3390 -> 3429 bytes
-rw-r--r--navit/support/espeak/espeak-data/gu_dictbin0 -> 5240 bytes
-rw-r--r--navit/support/espeak/espeak-data/hbs_dictbin7404 -> 7731 bytes
-rw-r--r--navit/support/espeak/espeak-data/hi_dictbin5696 -> 8606 bytes
-rw-r--r--navit/support/espeak/espeak-data/hu_dictbin5916 -> 113340 bytes
-rw-r--r--navit/support/espeak/espeak-data/hy_dictbin2469 -> 3400 bytes
-rw-r--r--navit/support/espeak/espeak-data/id_dictbin3083 -> 3082 bytes
-rw-r--r--navit/support/espeak/espeak-data/intonationsbin0 -> 1224 bytes
-rw-r--r--navit/support/espeak/espeak-data/is_dictbin5566 -> 5400 bytes
-rw-r--r--navit/support/espeak/espeak-data/it_dictbin48870 -> 61801 bytes
-rw-r--r--navit/support/espeak/espeak-data/jbo_dictbin2051 -> 2057 bytes
-rw-r--r--navit/support/espeak/espeak-data/ka_dictbin0 -> 3138 bytes
-rw-r--r--navit/support/espeak/espeak-data/kl_dictbin0 -> 2645 bytes
-rw-r--r--navit/support/espeak/espeak-data/kn_dictbin0 -> 5515 bytes
-rw-r--r--navit/support/espeak/espeak-data/ko_dictbin0 -> 6462 bytes
-rw-r--r--navit/support/espeak/espeak-data/ku_dictbin2277 -> 2268 bytes
-rw-r--r--navit/support/espeak/espeak-data/la_dictbin3911 -> 3817 bytes
-rw-r--r--navit/support/espeak/espeak-data/lfn_dictbin0 -> 2870 bytes
-rw-r--r--navit/support/espeak/espeak-data/lt_dictbin0 -> 5171 bytes
-rw-r--r--navit/support/espeak/espeak-data/lv_dictbin12558 -> 12293 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/af1_phtransbin1636 -> 1636 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/cr1_phtransbin2164 -> 2164 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/de2_phtransbin1444 -> 1540 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/de4_phtransbin1588 -> 1660 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/de6_phtransbin1204 -> 1276 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/ee1_phtransbin0 -> 1444 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/en1_phtransbin796 -> 796 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/es_phtransbin1708 -> 1708 bytes
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/mbrola_ph/fr1_phtransbin1852 -> 1972 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/gr2_phtransbin2212 -> 2212 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/hn1_phtransbin0 -> 532 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/hu1_phtransbin1420 -> 1444 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/ic1_phtransbin0 -> 1132 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/id1_phtransbin868 -> 892 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/in1_phtransbin1252 -> 1444 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/ir1_phtransbin0 -> 5812 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/it3_phtransbin892 -> 892 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/lt1_phtransbin0 -> 1060 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/lt2_phtransbin0 -> 1060 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/mx1_phtransbin0 -> 1804 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/mx2_phtransbin0 -> 1828 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/nl_phtransbin1612 -> 1684 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/pl1_phtransbin1540 -> 1564 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/pt1_phtransbin2092 -> 2092 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/ptbr4_phtransbin2356 -> 2356 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/ptbr_phtransbin2500 -> 2500 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/ro1_phtransbin2116 -> 2164 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/sv2_phtransbin1564 -> 1588 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/sv_phtransbin1564 -> 1588 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/tr1_phtransbin0 -> 364 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/us3_phtransbin1012 -> 1108 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/us_phtransbin1084 -> 1156 bytes
-rw-r--r--navit/support/espeak/espeak-data/mbrola_ph/vz_phtransbin0 -> 2284 bytes
-rw-r--r--navit/support/espeak/espeak-data/mk_dictbin4948 -> 4945 bytes
-rw-r--r--navit/support/espeak/espeak-data/ml_dictbin0 -> 4159 bytes
-rw-r--r--navit/support/espeak/espeak-data/ms_dictbin0 -> 12248 bytes
-rw-r--r--navit/support/espeak/espeak-data/nci_dictbin0 -> 1534 bytes
-rw-r--r--navit/support/espeak/espeak-data/ne_dictbin0 -> 10817 bytes
-rw-r--r--navit/support/espeak/espeak-data/nl_dictbin4124 -> 27197 bytes
-rw-r--r--navit/support/espeak/espeak-data/no_dictbin3735 -> 4178 bytes
-rw-r--r--navit/support/espeak/espeak-data/or_dictbin0 -> 4765 bytes
-rw-r--r--navit/support/espeak/espeak-data/pa_dictbin0 -> 6172 bytes
-rw-r--r--navit/support/espeak/espeak-data/pap_dictbin2148 -> 2128 bytes
-rw-r--r--navit/support/espeak/espeak-data/phondatabin355256 -> 406336 bytes
-rw-r--r--navit/support/espeak/espeak-data/phondata-manifest1546
-rw-r--r--navit/support/espeak/espeak-data/phonindexbin30256 -> 25028 bytes
-rw-r--r--navit/support/espeak/espeak-data/phontabbin36460 -> 40564 bytes
-rw-r--r--navit/support/espeak/espeak-data/pl_dictbin40458 -> 34924 bytes
-rw-r--r--navit/support/espeak/espeak-data/pt_dictbin14970 -> 24985 bytes
-rw-r--r--navit/support/espeak/espeak-data/ro_dictbin24961 -> 25205 bytes
-rw-r--r--navit/support/espeak/espeak-data/ru_dictbin5519 -> 6217 bytes
-rw-r--r--navit/support/espeak/espeak-data/si_dictbin0 -> 4143 bytes
-rw-r--r--navit/support/espeak/espeak-data/sk_dictbin8898 -> 9161 bytes
-rw-r--r--navit/support/espeak/espeak-data/sl_dictbin0 -> 3965 bytes
-rw-r--r--navit/support/espeak/espeak-data/sq_dictbin3222 -> 3199 bytes
-rw-r--r--navit/support/espeak/espeak-data/sv_dictbin9508 -> 9676 bytes
-rw-r--r--navit/support/espeak/espeak-data/sw_dictbin3072 -> 3029 bytes
-rw-r--r--navit/support/espeak/espeak-data/ta_dictbin2527 -> 110757 bytes
-rw-r--r--navit/support/espeak/espeak-data/te_dictbin0 -> 3380 bytes
-rw-r--r--navit/support/espeak/espeak-data/tr_dictbin4783 -> 6052 bytes
-rw-r--r--navit/support/espeak/espeak-data/ur_dictbin0 -> 18015 bytes
-rw-r--r--navit/support/espeak/espeak-data/vi_dictbin4855 -> 7438 bytes
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/f18
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/f21
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/!v/f30
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/!v/f40
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/f523
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/fast11
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/klatt4
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/klatt24
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/klatt34
-rw-r--r--navit/support/espeak/espeak-data/voices/!v/klatt44
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/m119
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/!v/m20
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/!v/m31
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/!v/m40
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/m727
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/!v/whisperf24
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/fa3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/fa-pin6
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/hi3
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/asia/hy (renamed from navit/support/espeak/espeak-data/voices/hy)0
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/asia/hy-west (renamed from navit/support/espeak/espeak-data/voices/hy-west)13
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/id (renamed from navit/support/espeak/espeak-data/voices/id)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/ka2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/kn5
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/ku (renamed from navit/support/espeak/espeak-data/voices/ku)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/ml6
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/ms17
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/ne5
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/pa2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/ta (renamed from navit/support/espeak/espeak-data/voices/ta)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/tr (renamed from navit/support/espeak/espeak-data/voices/tr)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/vi (renamed from navit/support/espeak/espeak-data/voices/vi)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/vi-hue12
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/vi-sgn12
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/asia/zh (renamed from navit/support/espeak/espeak-data/voices/zh)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/asia/zh-yue (renamed from navit/support/espeak/espeak-data/voices/zh-yue)3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/da3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/en (renamed from navit/support/espeak/espeak-data/voices/en/en)6
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/en-us (renamed from navit/support/espeak/espeak-data/voices/en/en-us)10
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/es-la7
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/an3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/bg5
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/bs (renamed from navit/support/espeak/espeak-data/voices/bs)0
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/europe/ca (renamed from navit/support/espeak/espeak-data/voices/ca)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/cs (renamed from navit/support/espeak/espeak-data/voices/cs)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/cy (renamed from navit/support/espeak/espeak-data/voices/cy)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/da7
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/el (renamed from navit/support/espeak/espeak-data/voices/el)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/es (renamed from navit/support/espeak/espeak-data/voices/es)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/et3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/fi (renamed from navit/support/espeak/espeak-data/voices/fi)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/fr-be9
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/ga4
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/hr (renamed from navit/support/espeak/espeak-data/voices/hr)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/hu (renamed from navit/support/espeak/espeak-data/voices/hu)4
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/is (renamed from navit/support/espeak/espeak-data/voices/is)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/it (renamed from navit/support/espeak/espeak-data/voices/it)4
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/lt5
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/lv (renamed from navit/support/espeak/espeak-data/voices/lv)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/mk (renamed from navit/support/espeak/espeak-data/voices/mk)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/nl (renamed from navit/support/espeak/espeak-data/voices/nl)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/no (renamed from navit/support/espeak/espeak-data/voices/no)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/pl (renamed from navit/support/espeak/espeak-data/voices/pl)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/pt-pt (renamed from navit/support/espeak/espeak-data/voices/pt-pt)3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/ro (renamed from navit/support/espeak/espeak-data/voices/ro)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/ru (renamed from navit/support/espeak/espeak-data/voices/ru)3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/sk (renamed from navit/support/espeak/espeak-data/voices/sk)0
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/europe/sq (renamed from navit/support/espeak/espeak-data/voices/sq)0
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/europe/sr (renamed from navit/support/espeak/espeak-data/voices/sr)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/europe/sv (renamed from navit/support/espeak/espeak-data/voices/sv)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/fr6
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/fr-be7
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/hi9
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-br12
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/mb/mb-br33
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/mb/mb-br42
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-cr11
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-de21
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-de38
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-de42
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-de4-en2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-de5-en2
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/mb/mb-de60
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/mb/mb-de6-grc0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-de71
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-ee18
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-en11
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-es11
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/mb/mb-es22
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-fr12
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-fr1-en1
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-fr41
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-fr4-en1
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-gr21
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-gr2-en3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-hu11
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-hu1-en1
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-ic17
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-id11
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-ir122
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-ir222
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-it31
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-it41
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-mx110
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-mx210
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-nl22
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-nl2-en2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-pl11
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/mb/mb-pt13
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-sw12
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-sw1-en2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-sw22
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-sw2-en4
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-tr17
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-tr210
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-us12
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-us23
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-us33
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/mb/mb-vz111
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/af (renamed from navit/support/espeak/espeak-data/voices/af)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/en-n (renamed from navit/support/espeak/espeak-data/voices/en/en-n)9
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/en-rp (renamed from navit/support/espeak/espeak-data/voices/en/en-rp)7
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/en-sc (renamed from navit/support/espeak/espeak-data/voices/en/en-sc)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/en-wi (renamed from navit/support/espeak/espeak-data/voices/en/en-wi)7
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/en-wm (renamed from navit/support/espeak/espeak-data/voices/en/en-wm)4
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/eo (renamed from navit/support/espeak/espeak-data/voices/eo)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/grc (renamed from navit/support/espeak/espeak-data/voices/test/grc)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/jbo4
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/other/la (renamed from navit/support/espeak/espeak-data/voices/la)0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/lfn6
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/other/sw (renamed from navit/support/espeak/espeak-data/voices/sw)2
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/pt3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/am3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/as4
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/az3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/bn3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/eu3
-rw-r--r--navit/support/espeak/espeak-data/voices/test/gd3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/gu3
-rw-r--r--navit/support/espeak/espeak-data/voices/test/jbo3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/kl3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/ko6
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/nci7
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/or2
-rwxr-xr-x[-rw-r--r--]navit/support/espeak/espeak-data/voices/test/pap0
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/si4
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/sl3
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/te5
-rwxr-xr-xnavit/support/espeak/espeak-data/voices/test/ur5
-rw-r--r--navit/support/espeak/espeak-data/zh_dictbin41834 -> 41827 bytes
-rw-r--r--navit/support/espeak/espeak-data/zhy_dictbin1556 -> 1556 bytes
-rw-r--r--navit/support/espeak/espeak.c314
-rw-r--r--navit/support/espeak/espeak_command.c50
-rw-r--r--navit/support/espeak/espeak_command.h42
-rw-r--r--navit/support/espeak/event.c145
-rw-r--r--navit/support/espeak/event.h4
-rw-r--r--navit/support/espeak/fifo.c166
-rw-r--r--navit/support/espeak/fifo.h10
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/intonation.c521
-rw-r--r--navit/support/espeak/klatt.c662
-rw-r--r--navit/support/espeak/klatt.h33
-rw-r--r--navit/support/espeak/mbrolib.h205
-rw-r--r--navit/support/espeak/mbrowrap.c613
-rw-r--r--navit/support/espeak/mbrowrap.h108
-rw-r--r--navit/support/espeak/numbers.c2185
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/phoneme.h72
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/phonemelist.c677
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/portaudio.h0
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/portaudio18.h0
-rw-r--r--navit/support/espeak/readclause.c1001
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/setlengths.c660
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/sintab.h0
-rw-r--r--navit/support/espeak/sonic.c974
-rw-r--r--navit/support/espeak/sonic.h138
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/speak.c177
-rw-r--r--navit/support/espeak/speak_init.c6
-rw-r--r--navit/support/espeak/speak_lib.c362
-rw-r--r--navit/support/espeak/speak_lib.h207
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/speech.h31
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/synth_mbrola.c504
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/synthdata.c1114
-rw-r--r--navit/support/espeak/synthdata.h20
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/synthesize.c818
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/synthesize.h347
-rw-r--r--navit/support/espeak/tr_languages.c1121
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/translate.c2060
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/translate.h466
-rw-r--r--navit/support/espeak/voice.h8
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/voices.c744
-rw-r--r--navit/support/espeak/voices.h19
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/wave.c460
-rw-r--r--navit/support/espeak/wave.h15
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/wave_pulse.c141
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/wave_sada.c22
-rw-r--r--[-rwxr-xr-x]navit/support/espeak/wavegen.c434
-rw-r--r--navit/support/espeak/wavegen.h21
309 files changed, 15711 insertions, 7119 deletions
diff --git a/navit/speech/espeak/speak.c b/navit/speech/espeak/speak.c
index 9ac7248b1..1aebcf318 100644
--- a/navit/speech/espeak/speak.c
+++ b/navit/speech/espeak/speak.c
@@ -68,7 +68,7 @@ int GetFileLength(const char *filename) {
return(statbuf.st_size);
}
-void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr) {
+void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr){
}
char *Alloc(int size) {
@@ -151,7 +151,7 @@ static BOOL initialise(void) {
int result;
WavegenInit(22050,0); // 22050
- if((result = LoadPhData()) != 1) {
+ if((result = LoadPhData(NULL)) != 1) {
if(result == -1) {
dbg(lvl_error, "Failed to load espeak-data");
return FALSE;
@@ -159,7 +159,7 @@ static BOOL initialise(void) {
dbg(lvl_error, "Wrong version of espeak-data 0x%x (expects 0x%x) at %s",result,version_phdata,path_home);
}
LoadConfig();
- SetVoiceStack(NULL);
+ SetVoiceStack(NULL, "");
SynthesizeInit();
for(param=0; param<N_SPEECH_PARAM; param++)
diff --git a/navit/support/espeak/CMakeLists.txt b/navit/support/espeak/CMakeLists.txt
index 55f461a28..c358f712e 100644
--- a/navit/support/espeak/CMakeLists.txt
+++ b/navit/support/espeak/CMakeLists.txt
@@ -2,8 +2,11 @@
if(INTERNAL_ESPEAK_COMPLETE)
set(ESPEAK_LIBRARY_ADDITIONAL speak_lib.c)
endif()
-supportlib_add_library(support_espeak compiledict.c dictionary.c intonation.c readclause.c setlengths.c
- numbers.c synth_mbrola.c synthdata.c synthesize.c translate.c tr_languages.c voices.c wavegen.c
- phonemelist.c klatt.c speak_init.c ${ESPEAK_LIBRARY_ADDITIONAL})
+supportlib_add_library(support_espeak compiledict.c dictionary.c espeak_command.c fifo.c klatt.c
+ numbers.c readclause.c sonic.c synthesize.c translate.c voices.c wavegen.c wave_sada.c
+ debug.c espeak.c event.c intonation.c mbrowrap.c phonemelist.c setlengths.c
+ synthdata.c synth_mbrola.c tr_languages.c wave.c wave_pulse.c ${ESPEAK_LIBRARY_ADDITIONAL})
+
+# speak.c
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/espeak-data DESTINATION ${SHARE_DIR} PATTERN ".svn" EXCLUDE)
diff --git a/navit/support/espeak/compiledict.c b/navit/support/espeak/compiledict.c
index a2b7865f9..af798ade2 100755..100644
--- a/navit/support/espeak/compiledict.c
+++ b/navit/support/espeak/compiledict.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -30,29 +30,48 @@
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
-
-//#define OPT_FORMAT // format the text and write formatted copy to Log file
-//#define OUTPUT_FORMAT
+#include "dictionary.h"
+#include "compiledict.h"
extern void Write4Bytes(FILE *f, int value);
-int HashDictionary(const char *string);
static FILE *f_log = NULL;
extern char *dir_dictionary;
+extern char word_phonemes[N_WORD_PHONEMES]; // a word translated into phoneme codes
+
static int linenum;
static int error_count;
-static int transpose_offset; // transpose character range for LookupDictList()
-static int transpose_min;
-static int transpose_max;
static int text_mode = 0;
static int debug_flag = 0;
+static int error_need_dictionary = 0;
static int hash_counts[N_HASH_DICT];
static char *hash_chains[N_HASH_DICT];
static char letterGroupsDefined[N_LETTER_GROUPS];
-#define __cdecl
+MNEM_TAB mnem_rules[] = {
+ {"unpr", DOLLAR_UNPR},
+ {"noprefix", DOLLAR_NOPREFIX}, // rule fails if a prefix has been removed
+ {"list", DOLLAR_LIST}, // a pronunciation is given in the *_list file
+
+ {"w_alt1", 0x11},
+ {"w_alt2", 0x12},
+ {"w_alt3", 0x13},
+ {"w_alt4", 0x14},
+ {"w_alt5", 0x15},
+ {"w_alt6", 0x16},
+ {"w_alt", 0x11}, // note: put longer names before their sub-strings
+
+ {"p_alt1", 0x21},
+ {"p_alt2", 0x22},
+ {"p_alt3", 0x23},
+ {"p_alt4", 0x24},
+ {"p_alt5", 0x25},
+ {"p_alt6", 0x26},
+ {"p_alt", 0x21},
+ {NULL, -1}
+};
MNEM_TAB mnem_flags[] = {
// these in the first group put a value in bits0-3 of dictionary_flags
@@ -74,40 +93,50 @@ MNEM_TAB mnem_flags[] = {
// these set the corresponding numbered bit if dictionary_flags
- {"$pause", 8}, /* ensure pause before this word */
- {"$only", 9}, /* only match on this word without suffix */
- {"$onlys", 10}, /* only match with none, or with 's' suffix */
- {"$strend", 11}, /* full stress if at end of clause */
- {"$strend2", 12}, /* full stress if at end of clause, or only followed by unstressed */
- {"$unstressend",13}, /* reduce stress at end of clause */
- {"$atend", 14}, /* use this pronunciation if at end of clause */
-
- {"$dot", 16}, /* ignore '.' after this word (abbreviation) */
- {"$abbrev", 17}, /* use this pronuciation rather than split into letters */
- {"$stem", 18}, // must have a suffix
+ {"$pause", 8}, // ensure pause before this word
+ {"$strend", 9}, // full stress if at end of clause
+ {"$strend2", 10}, // full stress if at end of clause, or only followed by unstressed
+ {"$unstressend",11}, // reduce stress at end of clause
+ {"$abbrev", 13}, // use this pronuciation rather than split into letters
// language specific
- {"$double", 19}, // IT double the initial consonant of next word
- {"$alt", 20}, // use alternative pronunciation
- {"$alt2", 21},
-
+ {"$double", 14}, // IT double the initial consonant of next word
+ {"$alt", 15}, // use alternative pronunciation
+ {"$alt1", 15}, // synonym for $alt
+ {"$alt2", 16},
+ {"$alt3", 17},
+ {"$alt4", 18},
+ {"$alt5", 19},
+ {"$alt6", 20},
+
+ {"$combine", 23}, // Combine with the next word
+
+ {"$dot", 24}, // ignore '.' after this word (abbreviation)
+ {"$hasdot", 25}, // use this pronunciation if there is a dot after the word
{"$max3", 27}, // limit to 3 repetitions
{"$brk", 28}, // a shorter $pause
{"$text", 29}, // word translates to replcement text, not phonemes
// flags in dictionary word 2
- {"$verbf", 0x20}, /* verb follows */
- {"$verbsf", 0x21}, /* verb follows, allow -s suffix */
- {"$nounf", 0x22}, /* noun follows */
- {"$pastf", 0x23}, /* past tense follows */
- {"$verb", 0x24}, /* use this pronunciation when its a verb */
- {"$noun", 0x25}, /* use this pronunciation when its a noun */
- {"$past", 0x26}, /* use this pronunciation when its past tense */
- {"$verbextend",0x28}, /* extend influence of 'verb follows' */
- {"$capital", 0x29}, /* use this pronunciation if initial letter is upper case */
- {"$allcaps", 0x2a}, /* use this pronunciation if initial letter is upper case */
+ {"$verbf", 0x20}, // verb follows
+ {"$verbsf", 0x21}, // verb follows, allow -s suffix
+ {"$nounf", 0x22}, // noun follows
+ {"$pastf", 0x23}, // past tense follows
+ {"$verb", 0x24}, // use this pronunciation when its a verb
+ {"$noun", 0x25}, // use this pronunciation when its a noun
+ {"$past", 0x26}, // use this pronunciation when its past tense
+ {"$verbextend",0x28}, // extend influence of 'verb follows'
+ {"$capital", 0x29}, // use this pronunciation if initial letter is upper case
+ {"$allcaps", 0x2a}, // use this pronunciation if initial letter is upper case
{"$accent", 0x2b}, // character name is base-character name + accent name
+ {"$sentence",0x2d}, // only if this clause is a sentence (i.e. terminator is {. ? !} not {, ; :}
+ {"$only", 0x2e}, // only match on this word without suffix
+ {"$onlys", 0x2f}, // only match with none, or with 's' suffix
+ {"$stem", 0x30}, // must have a suffix
+ {"$atend", 0x31}, // use this pronunciation if at end of clause
+ {"$atstart", 0x32}, // use this pronunciation at start of clause
+ {"$native", 0x33}, // not if we've switched translators
// doesn't set dictionary_flags
{"$?", 100}, // conditional rule, followed by byte giving the condition number
@@ -124,6 +153,7 @@ typedef struct {
char name[LEN_GROUP_NAME+1];
unsigned int start;
unsigned int length;
+ int group3_ix;
} RGROUP;
@@ -138,27 +168,6 @@ int isspace2(unsigned int c)
}
-static const char *LookupMnem2(MNEM_TAB *table, int value)
-{//=======================================================
- while(table->mnem != NULL)
- {
- if(table->value == value)
- return(table->mnem);
- table++;
- }
- return("");
-}
-
-
-char *print_dictionary_flags(unsigned int *flags)
-{//==============================================
- static char buf[20];
-
- sprintf(buf,"%s 0x%x/%x",LookupMnem2(mnem_flags,(flags[0] & 0xf)+0x40), flags[0], flags[1]);
- return(buf);
-}
-
-
static FILE *fopen_log(const char *fname,const char *access)
{//==================================================
@@ -174,20 +183,226 @@ static FILE *fopen_log(const char *fname,const char *access)
}
-#ifdef OPT_FORMAT
-static const char *lookup_mnem(MNEM_TAB *table, int value)
-//========================================================
+static const char *LookupMnemName(MNEM_TAB *table, const int value)
+//==========================================================
/* Lookup a mnemonic string in a table, return its name */
{
- while(table->mnem != NULL)
- {
- if(table->value==value)
- return(table->mnem);
- table++;
- }
- return("??"); /* not found */
-} /* end of mnem */
-#endif
+ while(table->mnem != NULL)
+ {
+ if(table->value==value)
+ return(table->mnem);
+ table++;
+ }
+ return(""); /* not found */
+} /* end of LookupMnemValue */
+
+
+void print_dictionary_flags(unsigned int *flags, char *buf, int buf_len)
+{//========================================================================
+ int stress;
+ int ix;
+ const char *name;
+ int len;
+ int total = 0;
+
+ buf[0] = 0;
+ if((stress = flags[0] & 0xf) != 0)
+ {
+ sprintf(buf, "%s", LookupMnemName(mnem_flags, stress + 0x40));
+ total = strlen(buf);
+ buf += total;
+ }
+
+ for(ix=8; ix<64; ix++)
+ {
+ if(((ix < 30) && (flags[0] & (1 << ix))) || ((ix >= 0x20) && (flags[1] & (1 << (ix-0x20)))))
+ {
+ name = LookupMnemName(mnem_flags, ix);
+ len = strlen(name) + 1;
+ total += len;
+ if(total >= buf_len)
+ continue;
+ sprintf(buf, " %s", name);
+ buf += len;
+ }
+ }
+}
+
+
+
+
+char *DecodeRule(const char *group_chars, int group_length, char *rule, int control)
+{//=================================================================================
+ /* Convert compiled match template to ascii */
+
+ unsigned char rb;
+ unsigned char c;
+ char *p;
+ char *p_end;
+ int ix;
+ int match_type;
+ int finished=0;
+ int value;
+ int linenum=0;
+ int flags;
+ int suffix_char;
+ int condition_num=0;
+ int at_start = 0;
+ const char *name;
+ char buf[200];
+ char buf_pre[200];
+ char suffix[20];
+ static char output[80];
+
+ static char symbols[] =
+ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+ '&','%','+','#','S','D','Z','A','L','!',' ','@','?','J','N','K','V','?','T','X','?','W'
+ };
+
+ static char symbols_lg[] = {'A','B','C','H','F','G','Y'};
+
+ match_type = 0;
+ buf_pre[0] = 0;
+
+ for(ix=0; ix<group_length; ix++)
+ {
+ buf[ix] = group_chars[ix];
+ }
+ buf[ix] = 0;
+
+ p = &buf[strlen(buf)];
+ while(!finished)
+ {
+ rb = *rule++;
+
+ if(rb <= RULE_LINENUM)
+ {
+ switch(rb)
+ {
+ case 0:
+ case RULE_PHONEMES:
+ finished=1;
+ break;
+ case RULE_PRE_ATSTART:
+ at_start = 1; // drop through to next case
+ case RULE_PRE:
+ match_type = RULE_PRE;
+ *p = 0;
+ p = buf_pre;
+ break;
+ case RULE_POST:
+ match_type = RULE_POST;
+ *p = 0;
+ strcat(buf," (");
+ p = &buf[strlen(buf)];
+ break;
+ case RULE_PH_COMMON:
+ break;
+ case RULE_CONDITION:
+ /* conditional rule, next byte gives condition number */
+ condition_num = *rule++;
+ break;
+ case RULE_LINENUM:
+ value = (rule[1] & 0xff) - 1;
+ linenum = (rule[0] & 0xff) - 1 + (value * 255);
+ rule+=2;
+ break;
+ }
+ continue;
+ }
+
+ if(rb == RULE_DOLLAR)
+ {
+ value = *rule++ & 0xff;
+ if((value != 0x01) || (control & FLAG_UNPRON_TEST))
+ {
+ // TODO write the string backwards if in RULE_PRE
+ p[0] = '$';
+ name = LookupMnemName(mnem_rules, value);
+ strcpy(&p[1],name);
+ p += (strlen(name)+1);
+ }
+ c = ' ';
+ }
+ else if(rb == RULE_ENDING)
+ {
+ static const char *flag_chars = "eipvdfq tba ";
+ flags = ((rule[0] & 0x7f)<< 8) + (rule[1] & 0x7f);
+ suffix_char = 'S';
+ if(flags & (SUFX_P >> 8))
+ suffix_char = 'P';
+ sprintf(suffix,"%c%d",suffix_char,rule[2] & 0x7f);
+ rule += 3;
+ for(ix=0; ix<9; ix++)
+ {
+ if(flags & 1)
+ sprintf(&suffix[strlen(suffix)],"%c",flag_chars[ix]);
+ flags = (flags >> 1);
+ }
+ strcpy(p,suffix);
+ p += strlen(suffix);
+ c = ' ';
+ }
+ else if(rb == RULE_LETTERGP)
+ {
+ c = symbols_lg[*rule++ - 'A'];
+ }
+ else if(rb == RULE_LETTERGP2)
+ {
+ value = *rule++ - 'A';
+ p[0] = 'L';
+ p[1] = (value / 10) + '0';
+ c = (value % 10) + '0';
+
+ if(match_type == RULE_PRE)
+ {
+ p[0] = c;
+ c = 'L';
+ }
+ p+=2;
+ }
+ else if(rb <= RULE_LAST_RULE)
+ c = symbols[rb];
+ else if(rb == RULE_SPACE)
+ c = '_';
+ else
+ c = rb;
+ *p++ = c;
+ }
+ *p = 0;
+
+ p = output;
+ p_end = p + sizeof(output) - 1;
+
+ if(linenum > 0)
+ {
+ sprintf(p,"%5d:\t",linenum);
+ p += 7;
+ }
+ if(condition_num > 0)
+ {
+ sprintf(p,"?%d ",condition_num);
+ p = &p[strlen(p)];
+ }
+ if(((ix = strlen(buf_pre)) > 0) || at_start)
+ {
+ if(at_start)
+ *p++ = '_';
+ while((--ix >= 0) && (p < p_end-3))
+ *p++ = buf_pre[ix];
+ *p++ = ')';
+ *p++ = ' ';
+ }
+ *p = 0;
+
+ buf[p_end - p] = 0; // prevent overflow in output[]
+ strcat(p,buf);
+ ix = strlen(output);
+ while(ix < 8)
+ output[ix++]=' ';
+ output[ix]=0;
+ return(output);
+} /* end of DecodeRule */
@@ -202,34 +417,30 @@ static int compile_line(char *linebuf, char *dict_line, int *hash)
unsigned int ix;
int step;
unsigned int n_flag_codes = 0;
+ int flagnum;
int flag_offset;
int length;
int multiple_words = 0;
int multiple_numeric_hyphen = 0;
char *multiple_string = NULL;
char *multiple_string_end = NULL;
-
+
int len_word;
int len_phonetic;
int text_not_phonemes; // this word specifies replacement text, not phonemes
unsigned int wc;
int all_upper_case;
-
+
char *mnemptr;
- char *comment;
unsigned char flag_codes[100];
char encoded_ph[200];
- unsigned char bad_phoneme[4];
-static char nullstring[] = {0};
+ char bad_phoneme_str[4];
+ int bad_phoneme;
+ static char nullstring[] = {0};
- comment = NULL;
text_not_phonemes = 0;
phonetic = word = nullstring;
-if(memcmp(linebuf,"_-",2)==0)
-{
-step=1; // TEST
-}
p = linebuf;
// while(isspace2(*p)) p++;
@@ -250,12 +461,12 @@ step=1; // TEST
#endif
step = 0;
-
+
c = 0;
while(c != '\n')
{
c = *p;
-
+
if((c == '?') && (step==0))
{
// conditional rule, allow only if the numbered condition is set for the voice
@@ -270,12 +481,12 @@ step=1; // TEST
}
ix = 0;
- if(isdigit(*p))
+ if(IsDigit09(*p))
{
ix += (*p-'0');
p++;
}
- if(isdigit(*p))
+ if(IsDigit09(*p))
{
ix = ix*10 + (*p-'0');
p++;
@@ -283,34 +494,32 @@ step=1; // TEST
flag_codes[n_flag_codes++] = ix + flag_offset;
c = *p;
}
-
+
if((c == '$') && isalnum(p[1]))
{
/* read keyword parameter */
mnemptr = p;
while(!isspace2(c = *p)) p++;
*p = 0;
-
- ix = LookupMnem(mnem_flags,mnemptr);
- if(ix > 0)
+
+ flagnum = LookupMnem(mnem_flags,mnemptr);
+ if(flagnum > 0)
{
- if(ix == 200)
+ if(flagnum == 200)
{
text_mode = 1;
}
- else
- if(ix == 201)
+ else if(flagnum == 201)
{
text_mode = 0;
}
- else
- if(ix == BITNUM_FLAG_TEXTMODE)
+ else if(flagnum == BITNUM_FLAG_TEXTMODE)
{
text_not_phonemes = 1;
}
else
{
- flag_codes[n_flag_codes++] = ix;
+ flag_codes[n_flag_codes++] = flagnum;
}
}
else
@@ -319,13 +528,12 @@ step=1; // TEST
error_count++;
}
}
-
+
if((c == '/') && (p[1] == '/') && (multiple_words==0))
{
c = '\n'; /* "//" treat comment as end of line */
- comment = p;
}
-
+
switch(step)
{
case 0:
@@ -335,22 +543,21 @@ step=1; // TEST
word = p+1;
step = 1;
}
- else
- if(!isspace2(c))
+ else if(!isspace2(c))
{
word = p;
step = 1;
}
break;
-
+
case 1:
- if((c == '-') && (word[0] != '_'))
+ if((c == '-') && multiple_words)
{
- if(isdigit(word[0]))
+ if(IsDigit09(word[0]))
{
multiple_numeric_hyphen = 1;
}
- else
+// else // ???
{
flag_codes[n_flag_codes++] = BITNUM_FLAG_HYPHENATED;
}
@@ -370,12 +577,20 @@ step=1; // TEST
step = 3;
}
}
- else
- if((c == ')') && multiple_words)
+ else if(c == ')')
{
- p[0] = 0;
- step = 3;
- multiple_words = 0;
+ if(multiple_words)
+ {
+ p[0] = 0;
+ multiple_words = 0;
+ step = 3;
+ }
+ else if(word[0] != '_')
+ {
+ fprintf(f_log, "%5d: Missing '('\n", linenum);
+ error_count++;
+ step = 3;
+ }
}
break;
@@ -384,15 +599,14 @@ step=1; // TEST
{
multiple_words++;
}
- else
- if(c == ')')
+ else if(c == ')')
{
p[0] = ' '; // terminate extra string
multiple_string_end = p+1;
step = 3;
}
break;
-
+
case 3:
if(!isspace2(c))
{
@@ -400,7 +614,7 @@ step=1; // TEST
step = 4;
}
break;
-
+
case 4:
if(isspace2(c))
{
@@ -408,68 +622,83 @@ step=1; // TEST
step = 5;
}
break;
-
+
case 5:
break;
}
p++;
}
-
+
if(word[0] == 0)
{
-#ifdef OPT_FORMAT
- if(comment != NULL)
- fprintf(f_log,"%s",comment);
- else
- fputc('\n',f_log);
-#endif
return(0); /* blank line */
}
if(text_mode)
text_not_phonemes = 1;
- if(text_not_phonemes != translator->langopts.textmode)
- {
- flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE;
- }
-
if(text_not_phonemes)
{
- // this is replacement text, so don't encode as phonemes. Restrict the length of the replacement word
- strncpy0(encoded_ph,phonetic,N_WORD_BYTES-4);
+ if(word[0] == '_')
+ {
+ // This is a special word, used by eSpeak. Translate this into phonemes now
+ strcat(phonetic, " "); // need a space to indicate word-boundary
+
+ // PROBLEM vowel reductions are not applied to the translated phonemes
+ // condition rules are not applied
+ TranslateWord(translator,phonetic,0,NULL,NULL);
+ text_not_phonemes = 0;
+ strncpy0(encoded_ph, word_phonemes, N_WORD_BYTES-4);
+
+ if((word_phonemes[0] == 0) && (error_need_dictionary < 3))
+ {
+ // the dictionary was not loaded, we need a second attempt
+ error_need_dictionary++;
+ fprintf(f_log,"%5d: Need to compile dictionary again\n",linenum);
+ }
+ {
+//char decoded_phonemes[128];
+//DecodePhonemes(word_phonemes,decoded_phonemes);
+//printf("Translator %x %s [%s] [%s]\n",translator->translator_name,word,phonetic,decoded_phonemes);
+ }
+ }
+ else
+ {
+ // this is replacement text, so don't encode as phonemes. Restrict the length of the replacement word
+ strncpy0(encoded_ph,phonetic,N_WORD_BYTES-4);
+ }
}
else
{
- EncodePhonemes(phonetic,encoded_ph,bad_phoneme);
+ EncodePhonemes(phonetic,encoded_ph,&bad_phoneme);
if(strchr(encoded_ph,phonSWITCH) != 0)
{
flag_codes[n_flag_codes++] = BITNUM_FLAG_ONLY_S; // don't match on suffixes (except 's') when switching languages
}
// check for errors in the phonemes codes
- for(ix=0; ix<sizeof(encoded_ph); ix++)
+ if(bad_phoneme != 0)
{
- c = encoded_ph[ix];
- if(c == 0) break;
-
- if(c == 255)
- {
- /* unrecognised phoneme, report error */
- fprintf(f_log,"%5d: Bad phoneme [%c] (0x%x) in: %s %s\n",linenum,bad_phoneme[0],bad_phoneme[0],word,phonetic);
- error_count++;
- }
+ // unrecognised phoneme, report error
+ bad_phoneme_str[utf8_out(bad_phoneme, bad_phoneme_str)] = 0;
+ fprintf(f_log,"%5d: Bad phoneme [%s] (U+%x) in: %s %s\n",linenum,bad_phoneme_str,bad_phoneme,word,phonetic);
+ error_count++;
}
}
+ if(text_not_phonemes != translator->langopts.textmode)
+ {
+ flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE;
+ }
+
+
if(sscanf(word,"U+%x",&wc) == 1)
{
// Character code
ix = utf8_out(wc, word);
word[ix] = 0;
}
- else
- if(word[0] != '_')
+ else if(word[0] != '_')
{
// convert to lower case, and note if the word is all-capitals
int c2;
@@ -483,9 +712,9 @@ step=1; // TEST
ix = utf8_in(&c2,p);
if(c2 == 0)
break;
- if(iswupper(c2))
+ if(iswupper2(c2))
{
- utf8_out(towlower(c2),p);
+ utf8_out(towlower2(c2),p);
}
else
{
@@ -501,14 +730,14 @@ step=1; // TEST
len_word = strlen(word);
- if(transpose_offset > 0)
+ if(translator->transpose_min > 0)
{
- len_word = TransposeAlphabet(word, transpose_offset, transpose_min, transpose_max);
+ len_word = TransposeAlphabet(translator, word);
}
*hash = HashDictionary(word);
len_phonetic = strlen(encoded_ph);
-
+
dict_line[1] = len_word; // bit 6 indicates whether the word has been compressed
len_word &= 0x3f;
@@ -525,7 +754,7 @@ step=1; // TEST
length = len_word + len_phonetic + 3;
strcpy(&dict_line[(len_word)+2],encoded_ph);
}
-
+
for(ix=0; ix<n_flag_codes; ix++)
{
dict_line[ix+length] = flag_codes[ix];
@@ -537,16 +766,15 @@ step=1; // TEST
if(multiple_words > 10)
{
fprintf(f_log,"%5d: Two many parts in a multi-word entry: %d\n",linenum,multiple_words);
+ error_count++;
}
else
{
- dict_line[length++] = 80 + multiple_words + multiple_numeric_hyphen; // if numeric, count a hyphen as an extra word
+ dict_line[length++] = 80 + multiple_words;
ix = multiple_string_end - multiple_string;
if(multiple_numeric_hyphen)
{
- // the first part is numeric, so keep the hyphen to match on
- dict_line[length++] = '-';
- dict_line[length++] = ' ';
+ dict_line[length++] = ' '; // ???
}
memcpy(&dict_line[length],multiple_string,ix);
length += ix;
@@ -554,35 +782,6 @@ step=1; // TEST
}
dict_line[0] = length;
-#ifdef OPT_FORMAT
- spaces = 16;
- for(ix=0; ix<n_flag_codes; ix++)
- {
- if(flag_codes[ix] >= 100)
- {
- fprintf(f_log,"?%d ",flag_codes[ix]-100);
- spaces -= 3;
- }
- }
-
- fprintf(f_log,"%s",word);
- spaces -= strlen(word);
- DecodePhonemes(encoded_ph,decoded_ph);
- while(spaces-- > 0) fputc(' ',f_log);
- spaces += (14 - strlen(decoded_ph));
-
- fprintf(f_log," %s",decoded_ph);
- while(spaces-- > 0) fputc(' ',f_log);
- for(ix=0; ix<n_flag_codes; ix++)
- {
- if(flag_codes[ix] < 100)
- fprintf(f_log," %s",lookup_mnem(mnem_flags,flag_codes[ix]));
- }
- if(comment != NULL)
- fprintf(f_log," %s",comment);
- else
- fputc('\n',f_log);
-#endif
return(length);
} /* end of compile_line */
@@ -630,12 +829,12 @@ static void compile_dictlist_end(FILE *f_out)
fflush(f_log);
#endif
}
-
+
for(hash=0; hash<N_HASH_DICT; hash++)
{
p = hash_chains[hash];
hash_counts[hash] = (int)ftell(f_out);
-
+
while(p != NULL)
{
length = *(p+sizeof(char *));
@@ -658,17 +857,22 @@ static int compile_dictlist_file(const char *path, const char* filename)
char buf[200];
char fname[sizeof(path_home)+45];
char dict_line[128];
-
+
text_mode = 0;
- sprintf(fname,"%s%s",path,filename);
+ // try with and without '.txt' extension
+ sprintf(fname,"%s%s.txt",path,filename);
if((f_in = fopen(fname,"r")) == NULL)
- return(-1);
+ {
+ sprintf(fname,"%s%s",path,filename);
+ if((f_in = fopen(fname,"r")) == NULL)
+ return(-1);
+ }
fprintf(f_log,"Compiling: '%s'\n",fname);
linenum=0;
-
+
while(fgets(buf,sizeof(buf),f_in) != NULL)
{
linenum++;
@@ -677,7 +881,7 @@ static int compile_dictlist_file(const char *path, const char* filename)
if(length == 0) continue; /* blank line */
hash_counts[hash]++;
-
+
p = (char *)malloc(length+sizeof(char *));
if(p == NULL)
{
@@ -688,13 +892,13 @@ static int compile_dictlist_file(const char *path, const char* filename)
}
break;
}
-
+
memcpy(p,&hash_chains[hash],sizeof(char *));
hash_chains[hash] = p;
memcpy(p+sizeof(char *),dict_line,length);
count++;
}
-
+
fprintf(f_log,"\t%d entries\n",count);
fclose(f_in);
return(0);
@@ -708,13 +912,26 @@ static char rule_post[80];
static char rule_match[80];
static char rule_phonemes[80];
static char group_name[LEN_GROUP_NAME+1];
+static int group3_ix;
#define N_RULES 2000 // max rules for each group
-static void copy_rule_string(char *string, int *state)
-{//===================================================
+static int isHexDigit(int c)
+{
+ if((c >= '0') && (c <= '9'))
+ return(c - '0');
+ if((c >= 'a') && (c <= 'f'))
+ return(c - 'a' + 10);
+ if((c >= 'A') && (c <= 'F'))
+ return(c - 'A' + 10);
+ return(-1);
+}
+
+
+static void copy_rule_string(char *string, int *state_out)
+{//=======================================================
// state 0: conditional, 1=pre, 2=match, 3=post, 4=phonemes
static char *outbuf[5] = {rule_cond, rule_pre, rule_match, rule_post, rule_phonemes};
static int next_state[5] = {2,2,4,4,4};
@@ -723,14 +940,18 @@ static void copy_rule_string(char *string, int *state)
int ix;
int len;
char c;
+ int c2, c3;
int sxflags;
int value;
int literal;
+ int hexdigit_input = 0;
+ int state = *state_out;
+ MNEM_TAB *mr;
if(string[0] == 0) return;
- output = outbuf[*state];
- if(*state==4)
+ output = outbuf[state];
+ if(state==4)
{
// append to any previous phoneme string, i.e. allow spaces in the phoneme string
len = strlen(rule_phonemes);
@@ -739,24 +960,44 @@ static void copy_rule_string(char *string, int *state)
output = &rule_phonemes[len];
}
sxflags = 0x808000; // to ensure non-zero bytes
-
+
for(p=string,ix=0;;)
{
literal = 0;
c = *p++;
+ if((c == '0') && (p[0] == 'x') && (isHexDigit(p[1]) >= 0) && (isHexDigit(p[2]) >= 0))
+ {
+ hexdigit_input = 1;
+ c = p[1];
+ p+= 2;
+ }
if(c == '\\')
{
c = *p++; // treat next character literally
+//#ifdef deleted
if((c >= '0') && (c <= '3') && (p[0] >= '0') && (p[0] <= '7') && (p[1] >= '0') && (p[1] <= '7'))
{
// character code given by 3 digit octal value;
c = (c-'0')*64 + (p[0]-'0')*8 + (p[1]-'0');
p += 2;
}
+//endif
literal = 1;
}
-
- if(((*state)==1) || ((*state)==3))
+ if(hexdigit_input)
+ {
+ if(((c2 = isHexDigit(c)) >= 0) && ((c3 = isHexDigit(p[0])) >= 0))
+ {
+ c = c2 * 16 + c3;
+ literal = 1;
+ p++;
+ }
+ else
+ {
+ hexdigit_input = 0;
+ }
+ }
+ if((state==1) || (state==3))
{
// replace special characters (note: 'E' is reserved for a replaced silent 'e')
if(literal == 0)
@@ -776,7 +1017,7 @@ static void copy_rule_string(char *string, int *state)
case 'H':
case 'F':
case 'G':
- if((*state) == 1)
+ if(state == 1)
{
// pre-rule, put the number before the RULE_LETTERGP;
output[ix++] = lettergp_letters[c-'A'] + 'A';
@@ -822,7 +1063,8 @@ static void copy_rule_string(char *string, int *state)
c = RULE_CAPITAL;
break;
case 'T':
- c = RULE_ALT1;
+ output[ix++] = RULE_DOLLAR;
+ c = 0x11;
break;
case 'W':
c = RULE_SPELLING;
@@ -830,6 +1072,9 @@ static void copy_rule_string(char *string, int *state)
case 'X':
c = RULE_NOVOWELS;
break;
+ case 'J':
+ c = RULE_SKIPCHARS;
+ break;
case 'L':
// expect two digits
c = *p++ - '0';
@@ -841,14 +1086,13 @@ static void copy_rule_string(char *string, int *state)
fprintf(f_log,"%5d: Expected 2 digits after 'L'\n",linenum);
error_count++;
}
- else
- if((c <= 0) || (c >= N_LETTER_GROUPS) || (letterGroupsDefined[(int)c] == 0))
+ else if((c <= 0) || (c >= N_LETTER_GROUPS) || (letterGroupsDefined[(int)c] == 0))
{
fprintf(f_log,"%5d: Letter group L%.2d not defined\n",linenum,c);
error_count++;
}
c += 'A';
- if((*state) == 1)
+ if(state == 1)
{
// pre-rule, put the group number before the RULE_LETTERGP command
output[ix++] = c;
@@ -860,10 +1104,40 @@ static void copy_rule_string(char *string, int *state)
}
break;
- case '$': // obsolete, replaced by S
- fprintf(f_log,"%5d: $ now not allowed, use S for suffix",linenum);
+ case '$':
+ value = 0;
+ mr = mnem_rules;
+ while(mr->mnem != NULL)
+ {
+ len = strlen(mr->mnem);
+ if(memcmp(p, mr->mnem, len) == 0)
+ {
+ value = mr->value;
+ p += len;
+ break;
+ }
+ mr++;
+ }
+
+ if(state == 1)
+ {
+ // pre-rule, put the number before the RULE_DOLLAR
+ output[ix++] = value;
+ c = RULE_DOLLAR;
+ }
+ else
+ {
+ output[ix++] = RULE_DOLLAR;
+ c = value;
+ }
+
+ if(value == 0)
+ {
+ fprintf(f_log,"%5d: $ command not recognized\n",linenum);
error_count++;
+ }
break;
+
case 'P':
sxflags |= SUFX_P; // Prefix, now drop through to Suffix
case 'S':
@@ -900,8 +1174,14 @@ static void copy_rule_string(char *string, int *state)
case 'b':
sxflags |= SUFX_B;
break;
+ case 'a':
+ sxflags |= SUFX_A;
+ break;
+ case 'm':
+ sxflags |= SUFX_M;
+ break;
default:
- if(isdigit(c))
+ if(IsDigit09(c))
value = (value*10) + (c - '0');
break;
}
@@ -918,7 +1198,7 @@ static void copy_rule_string(char *string, int *state)
if(c == 0) break;
}
- *state = next_state[*state];
+ *state_out = next_state[state];
} // end of copy_rule_string
@@ -932,12 +1212,13 @@ static char *compile_rule(char *input)
char *prule;
int len;
int len_name;
+ int start;
int state=2;
int finish=0;
- int pre_bracket=0;
char buf[80];
char output[150];
- unsigned char bad_phoneme[4];
+ int bad_phoneme;
+ char bad_phoneme_str[4];
buf[0]=0;
rule_cond[0]=0;
@@ -947,7 +1228,7 @@ static char *compile_rule(char *input)
rule_phonemes[0]=0;
p = buf;
-
+
for(ix=0; finish==0; ix++)
{
c = input[ix];
@@ -957,19 +1238,23 @@ static char *compile_rule(char *input)
case ')': // end of prefix section
*p = 0;
state = 1;
- pre_bracket = 1;
copy_rule_string(buf,&state);
p = buf;
break;
-
+
case '(': // start of suffix section
*p = 0;
state = 2;
copy_rule_string(buf,&state);
state = 3;
p = buf;
+ if(input[ix+1] == ' ')
+ {
+ fprintf(f_log,"%5d: Syntax error. Space after (\n",linenum);
+ error_count++;
+ }
break;
-
+
case '\n': // end of line
case '\r':
case 0: // end of line
@@ -977,14 +1262,14 @@ static char *compile_rule(char *input)
copy_rule_string(buf,&state);
finish=1;
break;
-
+
case '\t': // end of section section
case ' ':
*p = 0;
copy_rule_string(buf,&state);
p = buf;
break;
-
+
case '?':
if(state==2)
state=0;
@@ -997,27 +1282,30 @@ static char *compile_rule(char *input)
break;
}
}
-
+
if(strcmp(rule_match,"$group")==0)
strcpy(rule_match,group_name);
if(rule_match[0]==0)
- return(NULL);
-
- EncodePhonemes(rule_phonemes,buf,bad_phoneme);
- for(ix=0;; ix++)
{
- if((c = buf[ix])==0) break;
- if(c==255)
+ if(rule_post[0] != 0)
{
- fprintf(f_log,"%5d: Bad phoneme [%c] in %s",linenum,bad_phoneme[0],input);
+ fprintf(f_log,"%5d: Syntax error\n",linenum);
error_count++;
- break;
}
+ return(NULL);
+ }
+
+ EncodePhonemes(rule_phonemes,buf,&bad_phoneme);
+ if(bad_phoneme != 0)
+ {
+ bad_phoneme_str[utf8_out(bad_phoneme, bad_phoneme_str)] = 0;
+ fprintf(f_log,"%5d: Bad phoneme [%s] (U+%x) in: %s\n",linenum,bad_phoneme_str,bad_phoneme,input);
+ error_count++;
}
strcpy(output,buf);
len = strlen(buf)+1;
-
+
len_name = strlen(group_name);
if((len_name > 0) && (memcmp(rule_match,group_name,len_name) != 0))
{
@@ -1070,9 +1358,21 @@ static char *compile_rule(char *input)
}
if(rule_pre[0] != 0)
{
- output[len++] = RULE_PRE;
+ start = 0;
+ if(rule_pre[0] == RULE_SPACE)
+ {
+ // omit '_' at the beginning of the pre-string and imply it by using RULE_PRE_ATSTART
+ c = RULE_PRE_ATSTART;
+ start = 1;
+ }
+ else
+ {
+ c = RULE_PRE;
+ }
+ output[len++] = c;
+
// output PRE string in reverse order
- for(ix = strlen(rule_pre)-1; ix>=0; ix--)
+ for(ix = strlen(rule_pre)-1; ix>=start; ix--)
output[len++] = rule_pre[ix];
}
@@ -1089,21 +1389,24 @@ static char *compile_rule(char *input)
static int __cdecl string_sorter(char **a, char **b)
-{//=================================================
+{//===========================================
char *pa, *pb;
int ix;
- if((ix = strcmp(pa = *a,pb = *b)) != 0)
- return(ix);
+ if((ix = strcmp(pa = *a,pb = *b)) != 0)
+ return(ix);
pa += (strlen(pa)+1);
pb += (strlen(pb)+1);
- return(strcmp(pa,pb));
+ return(strcmp(pa,pb));
} /* end of string_sorter */
static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b)
{//===================================================
+// Sort long names before short names
int ix;
+ ix = strlen(b->name) - strlen(a->name);
+ if(ix != 0) return(ix);
ix = strcmp(a->name,b->name);
if(ix != 0) return(ix);
return(a->start-b->start);
@@ -1135,7 +1438,7 @@ static void print_rule_group(FILE *f_out, int n_rules, char **rules, char *name)
len1 = strlen(p) + 1;
p = &p[len1];
len2 = strlen(p);
-
+
rule_match[0]=0;
rule_pre[0]=0;
rule_post[0]=0;
@@ -1173,7 +1476,7 @@ static void print_rule_group(FILE *f_out, int n_rules, char **rules, char *name)
}
}
*pout = 0;
-
+
spaces = 12;
if(condition > 0)
{
@@ -1185,19 +1488,19 @@ static void print_rule_group(FILE *f_out, int n_rules, char **rules, char *name)
if(rule_pre[0] != 0)
{
p = buf;
- for(ix=strlen(rule_pre)-1;ix>=0;ix--)
+ for(ix=strlen(rule_pre)-1; ix>=0; ix--)
*p++ = rule_pre[ix];
sprintf(p,") ");
spaces -= strlen(buf);
for(ix=0; ix<spaces; ix++)
- fputc(' ',f_out);
+ fputc(' ',f_out);
fprintf(f_out,"%s",buf);
spaces = 0;
}
-
+
for(ix=0; ix<spaces; ix++)
fputc(' ',f_out);
-
+
spaces = 14;
sprintf(buf," %s ",rule_match);
if(rule_post[0] != 0)
@@ -1294,12 +1597,12 @@ static int compile_lettergroup(char *input, FILE *f_out)
int length;
int max_length = 0;
- #define N_LETTERGP_ITEMS 200
+#define N_LETTERGP_ITEMS 200
char *items[N_LETTERGP_ITEMS];
char item_length[N_LETTERGP_ITEMS];
p = input;
- if(!isdigit(p[0]) || !isdigit(p[1]))
+ if(!IsDigit09(p[0]) || !IsDigit09(p[1]))
{
fprintf(f_log,"%5d: Expected 2 digits after '.L'\n",linenum);
error_count++;
@@ -1336,6 +1639,7 @@ static int compile_lettergroup(char *input, FILE *f_out)
items[n_items] = p_start = p;
while((*p & 0xff) > ' ')
{
+ if (*p == '_') *p = ' '; // allow '_' for word break
p++;
}
*p++ = 0;
@@ -1375,16 +1679,18 @@ static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
int n_rules=0;
int count=0;
int different;
+ int wc;
const char *prev_rgroup_name;
unsigned int char_code;
int compile_mode=0;
char *buf;
- char buf1[200];
+ char buf1[500];
char *rules[N_RULES];
int n_rgroups = 0;
+ int n_groups3 = 0;
RGROUP rgroup[N_RULE_GROUP2];
-
+
linenum = 0;
group_name[0] = 0;
@@ -1400,7 +1706,7 @@ static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
if((p = (unsigned char *)strstr(buf,"//")) != NULL)
*p = 0;
- if(buf[0] == '\r') buf++; // ignore extra \r in \r\n
+ if(buf[0] == '\r') buf++; // ignore extra \r in \r\n
}
if((buf == NULL) || (buf[0] == '.'))
@@ -1410,6 +1716,7 @@ static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
if(n_rules > 0)
{
strcpy(rgroup[n_rgroups].name,group_name);
+ rgroup[n_rgroups].group3_ix = group3_ix;
rgroup[n_rgroups].start = ftell(f_temp);
output_rule_group(f_temp,n_rules,rules,group_name);
rgroup[n_rgroups].length = ftell(f_temp) - rgroup[n_rgroups].start;
@@ -1455,12 +1762,13 @@ static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
while((*p > ' ') && (ix < LEN_GROUP_NAME))
group_name[ix++] = *p++;
group_name[ix]=0;
-
+ group3_ix = 0;
+
if(sscanf(group_name,"0x%x",&char_code)==1)
{
// group character is given as a character code (max 16 bits)
p = (unsigned char *)group_name;
-
+
if(char_code > 0x100)
{
*p++ = (char_code >> 8);
@@ -1468,22 +1776,33 @@ static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
*p++ = char_code;
*p = 0;
}
-
- if(strlen(group_name) > 2)
+ else
+ {
+ if(translator->letter_bits_offset > 0)
+ {
+ utf8_in(&wc, group_name);
+ if(((ix = (wc - translator->letter_bits_offset)) >= 0) && (ix < 128))
+ {
+ group3_ix = ix+1; // not zero
+ }
+ }
+ }
+
+ if((group3_ix == 0) && (strlen(group_name) > 2))
{
if(utf8_in(&c,group_name) < 2)
{
fprintf(f_log,"%5d: Group name longer than 2 bytes (UTF8)",linenum);
error_count++;
}
-
+
group_name[2] = 0;
}
}
continue;
}
-
+
switch(compile_mode)
{
case 1: // .group
@@ -1495,37 +1814,37 @@ static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
break;
case 2: // .replace
+ {
+ int replace1;
+ int replace2;
+ char *p;
+
+ p = buf;
+ replace1 = 0;
+ replace2 = 0;
+ while(isspace2(*p)) p++;
+ ix = 0;
+ while((unsigned char)(*p) > 0x20) // not space or zero-byte
{
- int replace1;
- int replace2;
- char *p;
-
- p = buf;
- replace1 = 0;
- replace2 = 0;
- while(isspace2(*p)) p++;
- ix = 0;
- while((unsigned char)(*p) > 0x20) // not space or zero-byte
- {
- p += utf8_in(&c,p);
- replace1 += (c << ix);
- ix += 16;
- }
- while(isspace2(*p)) p++;
- ix = 0;
- while((unsigned char)(*p) > 0x20)
- {
- p += utf8_in(&c,p);
- replace2 += (c << ix);
- ix += 16;
- }
- if(replace1 != 0)
- {
- Write4Bytes(f_out,replace1); // write as little-endian
- Write4Bytes(f_out,replace2); // if big-endian, reverse the bytes in LoadDictionary()
- }
+ p += utf8_in(&c,p);
+ replace1 += (c << ix);
+ ix += 16;
}
- break;
+ while(isspace2(*p)) p++;
+ ix = 0;
+ while((unsigned char)(*p) > 0x20)
+ {
+ p += utf8_in(&c,p);
+ replace2 += (c << ix);
+ ix += 16;
+ }
+ if(replace1 != 0)
+ {
+ Write4Bytes(f_out,replace1); // write as little-endian
+ Write4Bytes(f_out,replace2); // if big-endian, reverse the bytes in LoadDictionary()
+ }
+ }
+ break;
}
}
fclose(f_temp);
@@ -1547,7 +1866,17 @@ static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
if(gp > 0)
fputc(RULE_GROUP_END,f_out);
fputc(RULE_GROUP_START,f_out);
- fprintf(f_out, prev_rgroup_name = rgroup[gp].name);
+
+ if(rgroup[gp].group3_ix != 0)
+ {
+ n_groups3++;
+ fputc(1,f_out);
+ fputc(rgroup[gp].group3_ix, f_out);
+ }
+ else
+ {
+ fprintf(f_out, "%s", prev_rgroup_name = rgroup[gp].name);
+ }
fputc(0,f_out);
}
@@ -1565,11 +1894,11 @@ static int compile_dictrules(FILE *f_in, FILE *f_out, char *fname_temp)
fputc(0,f_out);
fclose(f_temp);
-#if 0
+#ifndef _WIN32
remove(fname_temp);
#endif
- fprintf(f_log,"\t%d rules, %d groups\n\n",count,n_rgroups);
+ fprintf(f_log,"\t%d rules, %d groups (%d)\n\n",count,n_rgroups,n_groups3);
return(0);
} // end of compile_dictrules
@@ -1590,6 +1919,7 @@ int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, cha
char path[sizeof(path_home)+40]; // path_dsource+20
error_count = 0;
+ error_need_dictionary = 0;
memset(letterGroupsDefined,0,sizeof(letterGroupsDefined));
debug_flag = flags & 1;
@@ -1602,36 +1932,29 @@ int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, cha
if(f_log == NULL)
f_log = stderr;
+ // try with and without '.txt' extension
sprintf(path,"%s%s_",dsource,dict_name);
- sprintf(fname_in,"%srules",path);
- f_in = fopen_log(fname_in,"r");
- if(f_in == NULL)
+ sprintf(fname_in,"%srules.txt",path);
+ if((f_in = fopen(fname_in,"r")) == NULL)
{
- if(fname_err)
- strcpy(fname_err,fname_in);
- return(-1);
+ sprintf(fname_in,"%srules",path);
+ if((f_in = fopen_log(fname_in,"r")) == NULL)
+ {
+ if(fname_err)
+ strcpy(fname_err,fname_in);
+ return(-1);
+ }
}
sprintf(fname_out,"%s%c%s_dict",path_home,PATHSEP,dict_name);
if((f_out = fopen_log(fname_out,"wb+")) == NULL)
{
if(fname_err)
- strcpy(fname_err,fname_in);
+ strcpy(fname_err,fname_out);
return(-1);
}
sprintf(fname_temp,"%s%ctemp",path_home,PATHSEP);
- transpose_offset = 0;
-
- if(strcmp(dict_name,"ru") == 0)
- {
- // transpose cyrillic alphabet from unicode to iso8859-5
-// transpose_offset = 0x430-0xd0;
- transpose_offset = 0x42f; // range 0x01 to 0x22
- transpose_min = 0x430;
- transpose_max = 0x451;
- }
-
value = N_HASH_DICT;
Write4Bytes(f_out,value);
Write4Bytes(f_out,offset_rules);
@@ -1651,10 +1974,10 @@ int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, cha
compile_dictlist_file(path,"list");
}
compile_dictlist_file(path,"extra");
-
+
compile_dictlist_end(f_out);
offset_rules = ftell(f_out);
-
+
fprintf(f_log,"Compiling: '%s'\n",fname_in);
compile_dictrules(f_in,f_out,fname_temp);
@@ -1663,6 +1986,7 @@ int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, cha
fseek(f_out,4,SEEK_SET);
Write4Bytes(f_out,offset_rules);
fclose(f_out);
+ fflush(f_log);
LoadDictionary(translator, dict_name, 0);
diff --git a/navit/support/espeak/compiledict.h b/navit/support/espeak/compiledict.h
new file mode 100644
index 000000000..089115b37
--- /dev/null
+++ b/navit/support/espeak/compiledict.h
@@ -0,0 +1,20 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2010 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+void print_dictionary_flags(unsigned int *flags, char *buf, int buf_len);
+char *DecodeRule(const char *group_chars, int group_length, char *rule, int control);
diff --git a/navit/support/espeak/debug.h b/navit/support/espeak/debug.h
index c3fb9c920..199b2eb87 100644
--- a/navit/support/espeak/debug.h
+++ b/navit/support/espeak/debug.h
@@ -13,7 +13,7 @@ extern void debug_time(const char* text);
#else
-#ifdef PLATFORM_WINDOWS
+#ifdef NO_VARIADIC_MACROS
#define SHOW(format) // VC6 doesn't allow "..."
#else
#define SHOW(format,...)
diff --git a/navit/support/espeak/dictionary.c b/navit/support/espeak/dictionary.c
index d7dd3dc65..bfdbaae43 100755..100644
--- a/navit/support/espeak/dictionary.c
+++ b/navit/support/espeak/dictionary.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -19,8 +19,6 @@
#include "StdAfx.h"
-#define LOG_TRANSLATE
-
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
@@ -34,62 +32,65 @@
#include "phoneme.h"
#include "synthesize.h"
#include "translate.h"
-
+#include "dictionary.h"
+#include "compiledict.h"
int dictionary_skipwords;
char dictionary_name[40];
-extern char *print_dictionary_flags(unsigned int *flags);
-
// accented characters which indicate (in some languages) the start of a separate syllable
//static const unsigned short diereses_list[7] = {L'ä',L'ë',L'ï',L'ö',L'ü',L'ÿ',0};
static const unsigned short diereses_list[7] = {0xe4,0xeb,0xef,0xf6,0xfc,0xff,0};
// convert characters to an approximate 7 bit ascii equivalent
-// used for checking for vowels
-static unsigned char remove_accent[] = {
-'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i', // 0c0
-'d','n','o','o','o','o','o', 0, 'o','u','u','u','u','y','t','s', // 0d0
-'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i', // 0e0
-'d','n','o','o','o','o','o', 0 ,'o','u','u','u','u','y','t','y', // 0f0
-
-'a','a','a','a','a','a','c','c','c','c','c','c','c','c','d','d', // 100
-'d','d','e','e','e','e','e','e','e','e','e','e','g','g','g','g', // 110
-'g','g','g','g','h','h','h','h','i','i','i','i','i','i','i','i', // 120
-'i','i','i','i','j','j','k','k','k','l','l','l','l','l','l','l', // 130
-'l','l','l','n','n','n','n','n','n','n','n','n','o','o','o','o', // 140
-'o','o','o','o','r','r','r','r','r','r','s','s','s','s','s','s', // 150
-'s','s','t','t','t','t','t','t','u','u','u','u','u','u','u','u', // 160
-'u','u','u','u','w','w','y','y','y','z','z','z','z','z','z','s', // 170
-'b','b','b','b', 0, 0, 'o','c','c','d','d','d','d','d','e','e', // 180
-'e','f','f','g','g','h','i','i','k','k','l','l','m','n','n','o', // 190
-'o','o','o','o','p','p','y', 0, 0, 's','s','t','t','t','t','u', // 1a0
-'u','u','v','y','y','z','z','z','z','z','z','z', 0, 0, 0, 'w', // 1b0
-'t','t','t','k','d','d','d','l','l','l','n','n','n','a','a','i', // 1c0
-'i','o','o','u','u','u','u','u','u','u','u','u','u','e','a','a', // 1d0
-'a','a','a','a','g','g','g','g','k','k','o','o','o','o','z','z', // 1e0
-'j','d','d','d','g','g','w','w','n','n','a','a','a','a','o','o', // 1f0
-
-'a','a','a','a','e','e','e','e','i','i','i','i','o','o','o','o', // 200
-'r','r','r','r','u','u','u','u','s','s','t','t','y','y','h','h', // 210
-'n','d','o','o','z','z','a','a','e','e','o','o','o','o','o','o', // 220
-'o','o','y','y','l','n','t','j','d','q','a','c','c','l','t','s', // 230
-'z', 0 };
+// used for checking for vowels (up to 0x259=schwa)
+#define N_REMOVE_ACCENT 0x25e
+static unsigned char remove_accent[N_REMOVE_ACCENT] = {
+ 'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i', // 0c0
+ 'd','n','o','o','o','o','o', 0, 'o','u','u','u','u','y','t','s', // 0d0
+ 'a','a','a','a','a','a','a','c','e','e','e','e','i','i','i','i', // 0e0
+ 'd','n','o','o','o','o','o', 0 ,'o','u','u','u','u','y','t','y', // 0f0
+
+ 'a','a','a','a','a','a','c','c','c','c','c','c','c','c','d','d', // 100
+ 'd','d','e','e','e','e','e','e','e','e','e','e','g','g','g','g', // 110
+ 'g','g','g','g','h','h','h','h','i','i','i','i','i','i','i','i', // 120
+ 'i','i','i','i','j','j','k','k','k','l','l','l','l','l','l','l', // 130
+ 'l','l','l','n','n','n','n','n','n','n','n','n','o','o','o','o', // 140
+ 'o','o','o','o','r','r','r','r','r','r','s','s','s','s','s','s', // 150
+ 's','s','t','t','t','t','t','t','u','u','u','u','u','u','u','u', // 160
+ 'u','u','u','u','w','w','y','y','y','z','z','z','z','z','z','s', // 170
+ 'b','b','b','b', 0, 0, 'o','c','c','d','d','d','d','d','e','e', // 180
+ 'e','f','f','g','g','h','i','i','k','k','l','l','m','n','n','o', // 190
+ 'o','o','o','o','p','p','y', 0, 0, 's','s','t','t','t','t','u', // 1a0
+ 'u','u','v','y','y','z','z','z','z','z','z','z', 0, 0, 0, 'w', // 1b0
+ 't','t','t','k','d','d','d','l','l','l','n','n','n','a','a','i', // 1c0
+ 'i','o','o','u','u','u','u','u','u','u','u','u','u','e','a','a', // 1d0
+ 'a','a','a','a','g','g','g','g','k','k','o','o','o','o','z','z', // 1e0
+ 'j','d','d','d','g','g','w','w','n','n','a','a','a','a','o','o', // 1f0
+
+ 'a','a','a','a','e','e','e','e','i','i','i','i','o','o','o','o', // 200
+ 'r','r','r','r','u','u','u','u','s','s','t','t','y','y','h','h', // 210
+ 'n','d','o','o','z','z','a','a','e','e','o','o','o','o','o','o', // 220
+ 'o','o','y','y','l','n','t','j','d','q','a','c','c','l','t','s', // 230
+ 'z', 0, 0, 'b','u','v','e','e','j','j','q','q','r','r','y','y', // 240
+ 'a','a','a','b','o','c','d','d','e','e','e','e','e','e'
+};
+
void strncpy0(char *to,const char *from, int size)
-{//===============================================
- // strcpy with limit, ensures a zero terminator
+{//===================================================
+// strcpy with limit, ensures a zero terminator
strncpy(to,from,size);
to[size-1] = 0;
}
-static int reverse_word_bytes(int word)
-{//=============================
- // reverse the order of bytes from little-endian to big-endian
+int Reverse4Bytes(int word)
+{//==========================
+// reverse the order of bytes from little-endian to big-endian
#ifdef ARCH_BIG
int ix;
int word2 = 0;
@@ -106,8 +107,8 @@ static int reverse_word_bytes(int word)
}
-int LookupMnem(MNEM_TAB *table, char *string)
-{//==========================================
+int LookupMnem(MNEM_TAB *table, const char *string)
+{//==================================================
while(table->mnem != NULL)
{
if(strcmp(string,table->mnem)==0)
@@ -118,7 +119,6 @@ int LookupMnem(MNEM_TAB *table, char *string)
}
-
//=============================================================================================
// Read pronunciation rules and pronunciation lookup dictionary
//
@@ -127,9 +127,9 @@ int LookupMnem(MNEM_TAB *table, char *string)
static void InitGroups(Translator *tr)
{//===================================
-/* Called after dictionary 1 is loaded, to set up table of entry points for translation rule chains
- for single-letters and two-letter combinations
-*/
+// Called after dictionary 1 is loaded, to set up table of entry points for translation rule chains
+// for single-letters and two-letter combinations
+
int ix;
char *p;
@@ -146,6 +146,7 @@ static void InitGroups(Translator *tr)
tr->groups2_start[ix]=255; // indicates "not set"
}
memset(tr->letterGroups,0,sizeof(tr->letterGroups));
+ memset(tr->groups3,0,sizeof(tr->groups3));
p = tr->data_dictrules;
while(*p != 0)
@@ -159,7 +160,7 @@ static void InitGroups(Translator *tr)
if(p[0] == RULE_REPLACEMENTS)
{
- pw = (unsigned int *)(((long)p+4) & ~3); // advance to next word boundary
+ pw = (unsigned int *)(((long64)p+4) & ~3); // advance to next word boundary
tr->langopts.replace_chars = pw;
while(pw[0] != 0)
{
@@ -171,9 +172,9 @@ static void InitGroups(Translator *tr)
pw = (unsigned int *)(tr->langopts.replace_chars);
while(*pw != 0)
{
- *pw = reverse_word_bytes(*pw);
+ *pw = Reverse4Bytes(*pw);
pw++;
- *pw = reverse_word_bytes(*pw);
+ *pw = Reverse4Bytes(*pw);
pw++;
}
#endif
@@ -194,25 +195,29 @@ static void InitGroups(Translator *tr)
len = strlen(p);
p_name = p;
c = p_name[0];
-
+ c2 = p_name[1];
+
p += (len+1);
if(len == 1)
{
tr->groups1[c] = p;
}
- else
- if(len == 0)
+ else if(len == 0)
{
tr->groups1[0] = p;
}
+ else if(c == 1)
+ {
+ // index by offset from letter base
+ tr->groups3[c2 - 1] = p;
+ }
else
{
if(tr->groups2_start[c] == 255)
tr->groups2_start[c] = tr->n_groups2;
-
+
tr->groups2_count[c]++;
tr->groups2[tr->n_groups2] = p;
- c2 = p_name[1];
tr->groups2_name[tr->n_groups2++] = (c + (c2 << 8));
}
}
@@ -240,9 +245,7 @@ int LoadDictionary(Translator *tr, const char *name, int no_error)
char fname[sizeof(path_home)+20];
strcpy(dictionary_name,name); // currently loaded dictionary name
-
- if(no_error) // don't load dictionary, just set the dictionary_name
- return(1);
+ strcpy(tr->dictionary_name, name);
// Load a pronunciation data file into memory
// bytes 0-3: offset to rules data
@@ -250,6 +253,12 @@ int LoadDictionary(Translator *tr, const char *name, int no_error)
sprintf(fname,"%s%c%s_dict",path_home,PATHSEP,name);
size = GetFileLength(fname);
+ if(tr->data_dictlist != NULL)
+ {
+ Free(tr->data_dictlist);
+ tr->data_dictlist = NULL;
+ }
+
f = fopen(fname,"rb");
if((f == NULL) || (size <= 0))
{
@@ -260,16 +269,13 @@ int LoadDictionary(Translator *tr, const char *name, int no_error)
return(1);
}
- if(tr->data_dictlist != NULL)
- Free(tr->data_dictlist);
-
tr->data_dictlist = Alloc(size);
- fread(tr->data_dictlist,size,1,f);
+ size = fread(tr->data_dictlist,1,size,f);
fclose(f);
pw = (int *)(tr->data_dictlist);
- length = reverse_word_bytes(pw[1]);
+ length = Reverse4Bytes(pw[1]);
if(size <= (N_HASH_DICT + sizeof(int)*2))
{
@@ -277,20 +283,16 @@ int LoadDictionary(Translator *tr, const char *name, int no_error)
return(2);
}
- if((reverse_word_bytes(pw[0]) != N_HASH_DICT) ||
- (length <= 0) || (length > 0x8000000))
+ if((Reverse4Bytes(pw[0]) != N_HASH_DICT) ||
+ (length <= 0) || (length > 0x8000000))
{
- fprintf(stderr,"Bad data: '%s' (%x length=%x)\n",fname,reverse_word_bytes(pw[0]),length);
+ fprintf(stderr,"Bad data: '%s' (%x length=%x)\n",fname,Reverse4Bytes(pw[0]),length);
return(2);
}
tr->data_dictrules = &(tr->data_dictlist[length]);
// set up indices into data_dictrules
InitGroups(tr);
- if(tr->groups1[0] == NULL)
- {
- fprintf(stderr,"Error in %s_rules, no default rule group\n",name);
- }
// set up hash table for data_dictlist
p = &(tr->data_dictlist[8]);
@@ -305,6 +307,11 @@ int LoadDictionary(Translator *tr, const char *name, int no_error)
p++; // skip over the zero which terminates the list for this hash value
}
+ if((tr->dict_min_size > 0) && (size < (unsigned int)tr->dict_min_size))
+ {
+ fprintf(stderr, "Full dictionary is not installed for '%s'\n", name);
+ }
+
return(0);
} // end of LoadDictionary
@@ -315,18 +322,18 @@ int HashDictionary(const char *string)
This is used to access the dictionary_2 word-lookup dictionary
*/
{
- int c;
+ int c;
int chars=0;
- int hash=0;
+ int hash=0;
- while((c = (*string++ & 0xff)) != 0)
- {
- hash = hash * 8 + c;
- hash = (hash & 0x3ff) ^ (hash >> 8); /* exclusive or */
+ while((c = (*string++ & 0xff)) != 0)
+ {
+ hash = hash * 8 + c;
+ hash = (hash & 0x3ff) ^ (hash >> 8); /* exclusive or */
chars++;
- }
+ }
- return((hash+chars) & 0x3ff); // a 10 bit hash code
+ return((hash+chars) & 0x3ff); // a 10 bit hash code
} // end of HashDictionary
@@ -338,12 +345,12 @@ int HashDictionary(const char *string)
-char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme)
-/*********************************************************************/
+const char *EncodePhonemes(const char *p, char *outptr, int *bad_phoneme)
+/******************************************************************/
/* Translate a phoneme string from ascii mnemonics to internal phoneme numbers,
from 'p' up to next blank .
Returns advanced 'p'
- outptr contains encoded phonemes, unrecognised phonemes are encoded as 255
+ outptr contains encoded phonemes, unrecognized phoneme stops the encoding
bad_phoneme must point to char array of length 2 of more
*/
{
@@ -355,7 +362,8 @@ char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme)
int consumed;
unsigned int mnemonic_word;
- bad_phoneme[0] = 0;
+ if(bad_phoneme != NULL)
+ *bad_phoneme = 0;
// skip initial blanks
while(isspace(*p))
@@ -376,7 +384,7 @@ char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme)
if((c = p[1]) == '|')
{
// treat double || as a word-break symbol, drop through
- // to the default case with c = '|'
+ // to the default case with c = '|'
}
else
{
@@ -401,11 +409,11 @@ char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme)
mnemonic_word = phoneme_tab[ix]->mnemonic;
while(((c = p[count]) > ' ') && (count < 4) &&
- (c == ((mnemonic_word >> (count*8)) & 0xff)))
+ (c == ((mnemonic_word >> (count*8)) & 0xff)))
count++;
if((count > max) &&
- ((count == 4) || (((mnemonic_word >> (count*8)) & 0xff)==0)))
+ ((count == 4) || (((mnemonic_word >> (count*8)) & 0xff)==0)))
{
max = count;
max_ph = phoneme_tab[ix]->code;
@@ -414,9 +422,13 @@ char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme)
if(max_ph == 0)
{
- max_ph = 255; /* not recognised */
- bad_phoneme[0] = *p;
- bad_phoneme[1] = 0;
+ // not recognised, report and ignore
+ if(bad_phoneme != NULL)
+ {
+ utf8_in(bad_phoneme, p);
+ }
+ *outptr++ = 0;
+ return(p+1);
}
if(max <= 0)
@@ -458,23 +470,23 @@ char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme)
void DecodePhonemes(const char *inptr, char *outptr)
-//==================================================
+{//==================================================
// Translate from internal phoneme codes into phoneme mnemonics
-{
unsigned char phcode;
unsigned char c;
unsigned int mnem;
PHONEME_TAB *ph;
static const char *stress_chars = "==,,'* ";
+ sprintf(outptr,"* ");
while((phcode = *inptr++) > 0)
{
if(phcode == 255)
continue; /* indicates unrecognised phoneme */
if((ph = phoneme_tab[phcode]) == NULL)
continue;
-
- if((ph->type == phSTRESS) && (ph->std_length <= 4) && (ph->spect == 0))
+
+ if((ph->type == phSTRESS) && (ph->std_length <= 4) && (ph->program == 0))
{
if(ph->std_length > 1)
*outptr++ = stress_chars[ph->std_length];
@@ -483,7 +495,7 @@ void DecodePhonemes(const char *inptr, char *outptr)
{
mnem = ph->mnemonic;
- while((c = (mnem & 0xff)) != 0)
+ while((c = (mnem & 0xff)) != 0)
{
*outptr++ = c;
mnem = mnem >> 8;
@@ -501,85 +513,273 @@ void DecodePhonemes(const char *inptr, char *outptr)
} // end of DecodePhonemes
+// using Kirschenbaum to IPA translation, ascii 0x20 to 0x7f
+unsigned short ipa1[96] = {
+ 0x20,0x21,0x22,0x2b0,0x24,0x25,0x0e6,0x2c8,0x28,0x29,0x27e,0x2b,0x2cc,0x2d,0x2e,0x2f,
+ 0x252,0x31,0x32,0x25c,0x34,0x35,0x36,0x37,0x275,0x39,0x2d0,0x2b2,0x3c,0x3d,0x3e,0x294,
+ 0x259,0x251,0x3b2,0xe7,0xf0,0x25b,0x46,0x262,0x127,0x26a,0x25f,0x4b,0x26b,0x271,0x14b,0x254,
+ 0x3a6,0x263,0x280,0x283,0x3b8,0x28a,0x28c,0x153,0x3c7,0xf8,0x292,0x32a,0x5c,0x5d,0x5e,0x5f,
+ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x261,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x303,0x7f
+};
-static void WriteMnemonic(char *phon_out, int *ix, int mnem)
-{//=========================================================
- unsigned char c;
+#define N_PHON_OUT 500 // realloc increment
+static char *phon_out_buf = NULL; // passes the result of GetTranslatedPhonemeString()
+static int phon_out_size = 0;
+
+
+char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags)
+{//===================================================================================================
+ int c;
+ int mnem;
+ int len;
+ int first;
+ int ix = 0;
+ char *p;
+ PHONEME_DATA phdata;
+
+ if(ph->code == phonEND_WORD)
+ {
+ // ignore
+ phon_out[0] = 0;
+ return(phon_out);
+ }
+
+ if(ph->code == phonSWITCH)
+ {
+ // the tone_ph field contains a phoneme table number
+ p = phoneme_tab_list[plist->tone_ph].name;
+ sprintf(phon_out, "(%s)", p);
+ return(phon_out + strlen(phon_out));
+ }
+
+ if(use_ipa)
+ {
+ // has an ipa name been defined for this phoneme ?
+ phdata.ipa_string[0] = 0;
+
+ if(plist == NULL)
+ {
+ InterpretPhoneme2(ph->code, &phdata);
+ }
+ else
+ {
+ InterpretPhoneme(NULL, 0, plist, &phdata, NULL);
+ }
+
+ p = phdata.ipa_string;
+ if(*p == 0x20)
+ {
+ // indicates no name for this phoneme
+ *phon_out = 0;
+ return(phon_out);
+ }
+ if((*p != 0) && ((*p & 0xff) < 0x20))
+ {
+ // name starts with a flags byte
+ if(flags != NULL)
+ *flags = *p;
+ p++;
+ }
+
+ len = strlen(p);
+ if(len > 0)
+ {
+ strcpy(phon_out, p);
+ phon_out += len;
+ *phon_out = 0;
+ return(phon_out);
+ }
+ }
- while((c = mnem & 0xff) != 0)
+ first = 1;
+ for(mnem = ph->mnemonic; (c = mnem & 0xff) != 0; mnem = mnem >> 8)
{
if((c == '/') && (option_phoneme_variants==0))
break; // discard phoneme variant indicator
- phon_out[(*ix)++]= c;
- // phon_out[phon_out_ix++]= ipa1[c];
- mnem = mnem >> 8;
+
+ if(use_ipa)
+ {
+ // convert from ascii to ipa
+ if(first && (c == '_'))
+ break; // don't show pause phonemes
+
+ if((c == '#') && (ph->type == phVOWEL))
+ break; // # is subscript-h, but only for consonants
+
+ // ignore digits after the first character
+ if(!first && IsDigit09(c))
+ continue;
+
+ if((c >= 0x20) && (c < 128))
+ c = ipa1[c-0x20];
+
+ ix += utf8_out(c, &phon_out[ix]);
+ }
+ else
+ {
+ phon_out[ix++]= c;
+ }
+ first = 0;
}
-}
+ phon_out = &phon_out[ix];
+ *phon_out = 0;
+ return(phon_out);
+} // end of WritePhMnemonic
-void GetTranslatedPhonemeString(char *phon_out, int n_phon_out)
-{//============================================================
-/* Can be called after a clause has been translated into phonemes, in order
- to display the clause in phoneme mnemonic form.
-*/
+
+const char *GetTranslatedPhonemeString(int phoneme_mode)
+{//=======================================================
+ /* Called after a clause has been translated into phonemes, in order
+ to display the clause in phoneme mnemonic form.
+
+ phoneme_mode bits 0-3: 0=only phoneme names, 1=ties, 2=ZWJ, 3=underscore separator
+ bit 4: 0=eSpeak phoneme names, 1=IPA
+ */
int ix;
+ unsigned int len;
int phon_out_ix=0;
int stress;
+ int c;
char *p;
+ char *buf;
+ int count;
+ int flags;
+ int use_ipa;
+ int use_tie;
+ int separate_phonemes = 0;
+ char phon_buf[30];
+ char phon_buf2[30];
PHONEME_LIST *plist;
-
+
static const char *stress_chars = "==,,''";
+ static const int char_tie[] = {0x0361, 0x200d}; // combining-double-inverted-breve, zero-width-joiner
+
+ use_ipa = phoneme_mode & 0x10;
+ use_tie = phoneme_mode & 0x0f;
+
+ if(phon_out_buf == NULL)
+ {
+ phon_out_size = N_PHON_OUT;
+ if((phon_out_buf = (char *)realloc(phon_out_buf, phon_out_size)) == NULL)
+ {
+ phon_out_size = 0;
+ return("");
+ }
+ }
+
+ if(use_tie >= 3)
+ {
+ // separate individual phonemes with underscores
+ separate_phonemes = '_';
+ use_tie = 0;
+ }
- if(phon_out != NULL)
+
+ for(ix=1; ix<(n_phoneme_list-2); ix++)
{
- for(ix=1; ix<(n_phoneme_list-2) && (phon_out_ix < (n_phon_out - 6)); ix++)
+ buf = phon_buf;
+
+ plist = &phoneme_list[ix];
+
+ WritePhMnemonic(phon_buf2, plist->ph, plist, use_ipa, &flags);
+ if(plist->newword)
+ *buf++ = ' ';
+ else
{
- plist = &phoneme_list[ix];
- if(plist->newword)
- phon_out[phon_out_ix++] = ' ';
+ if((separate_phonemes != 0) && (ix > 1))
+ {
+ utf8_in(&c, phon_buf2);
+ if((c < 0x2b0) || (c > 0x36f)) // not if the phoneme starts with a superscript letter
+ {
+ *buf++ = separate_phonemes;
+ }
+ }
+ }
- if(plist->synthflags & SFLAG_SYLLABLE)
+ if(plist->synthflags & SFLAG_SYLLABLE)
+ {
+ if((stress = plist->stresslevel) > 1)
{
- if((stress = plist->stresslevel) > 1)
+ c = 0;
+ if(stress > 5) stress = 5;
+
+ if(use_ipa)
+ {
+ c = 0x2cc; // ipa, secondary stress
+ if(stress > 3)
+ c = 0x02c8; // ipa, primary stress
+ }
+ else
+ {
+ c = stress_chars[stress];
+ }
+
+ if(c != 0)
{
- if(stress > 5) stress = 5;
- phon_out[phon_out_ix++] = stress_chars[stress];
+ buf += utf8_out(c, buf);
+// if(separate_phonemes)
+// *buf++ = separate_phonemes;
}
}
- WriteMnemonic(phon_out, &phon_out_ix, plist->ph->mnemonic);
+ }
+ flags = 0;
+ count = 0;
+ for(p=phon_buf2; *p != 0;)
+ {
+ p += utf8_in(&c, p);
+ if(use_tie > 0)
+ {
+ // look for non-inital alphabetic character, but not diacritic, superscript etc.
+ if((count>0) && !(flags & (1 << (count-1))) && ((c < 0x2b0) || (c > 0x36f)) && iswalpha2(c))
+ {
+ buf += utf8_out(char_tie[use_tie-1], buf);
+ }
+ }
+ buf += utf8_out(c, buf);
+ count++;
+ }
+
+ if(plist->ph->code != phonSWITCH)
+ {
if(plist->synthflags & SFLAG_LENGTHEN)
{
- WriteMnemonic(phon_out, &phon_out_ix, phoneme_tab[phonLENGTHEN]->mnemonic);
+ buf = WritePhMnemonic(buf, phoneme_tab[phonLENGTHEN], NULL, use_ipa, NULL);
}
if((plist->synthflags & SFLAG_SYLLABLE) && (plist->type != phVOWEL))
{
// syllablic consonant
- WriteMnemonic(phon_out, &phon_out_ix, phoneme_tab[phonSYLLABIC]->mnemonic);
+ buf = WritePhMnemonic(buf, phoneme_tab[phonSYLLABIC], NULL, use_ipa, NULL);
}
- if(plist->ph->code == phonSWITCH)
+ if(plist->tone_ph > 0)
{
- // the tone_ph field contains a phoneme table number
- p = phoneme_tab_list[plist->tone_ph].name;
- while(*p != 0)
- {
- phon_out[phon_out_ix++] = *p++;
- }
- phon_out[phon_out_ix++] = ' ';
+ buf = WritePhMnemonic(buf, phoneme_tab[plist->tone_ph], NULL, use_ipa, NULL);
}
- else
- if(plist->tone_ph > 0)
+ }
+
+ len = buf - phon_buf;
+ if((phon_out_ix + len) >= phon_out_size)
+ {
+ // enlarge the phoneme buffer
+ phon_out_size = phon_out_ix + len + N_PHON_OUT;
+ if((phon_out_buf = (char *)realloc(phon_out_buf, phon_out_size)) == NULL)
{
- WriteMnemonic(phon_out, &phon_out_ix, phoneme_tab[plist->tone_ph]->mnemonic);
+ phon_out_size = 0;
+ return("");
}
}
-
- if(phon_out_ix >= n_phon_out)
- phon_out_ix = n_phon_out - 1;
- phon_out[phon_out_ix] = 0;
+
+ phon_buf[len] = 0;
+ strcpy(&phon_out_buf[phon_out_ix], phon_buf);
+ phon_out_ix += len;
}
+ phon_out_buf[phon_out_ix] = 0;
+
+ return(phon_out_buf);
} // end of GetTranslatedPhonemeString
@@ -593,7 +793,7 @@ void GetTranslatedPhonemeString(char *phon_out, int n_phon_out)
static int IsLetterGroup(Translator *tr, char *word, int group, int pre)
{//=====================================================================
- // match the word against a list of utf-8 strings
+// match the word against a list of utf-8 strings
char *p;
char *w;
int len=0;
@@ -613,7 +813,7 @@ static int IsLetterGroup(Translator *tr, char *word, int group, int pre)
{
w = word;
}
- while(*p == *w)
+ while((*p == *w) && (*w != 0))
{
w++;
p++;
@@ -647,137 +847,63 @@ static int IsLetter(Translator *tr, int letter, int group)
if(tr->letter_bits_offset > 0)
{
- if(((letter2 = (letter - tr->letter_bits_offset)) > 0) && (letter2 < 0x80))
- letter = letter2;
+ if(((letter2 = (letter - tr->letter_bits_offset)) > 0) && (letter2 < 0x100))
+ letter = letter2;
else
return(0);
}
else
{
- if((letter >= 0xc0) && (letter <= 0x241))
+ if((letter >= 0xc0) && (letter < N_REMOVE_ACCENT))
return(tr->letter_bits[remove_accent[letter-0xc0]] & (1L << group));
}
- if((letter >= 0) && (letter < 0x80))
+ if((letter >= 0) && (letter < 0x100))
return(tr->letter_bits[letter] & (1L << group));
return(0);
}
-static int IsVowel(Translator *tr, int letter)
-{//===========================================
- return(IsLetter(tr, letter, 0));
+int IsVowel(Translator *tr, int letter)
+{//====================================
+ return(IsLetter(tr, letter, LETTERGP_VOWEL2));
}
-static int Unpronouncable_en(Translator *tr, char *word)
-{//=====================================================
-/* Determines whether a word in 'unpronouncable', i.e. whether it should
- be spoken as individual letters.
-
- This function is language specific.
-*/
-
+static int Unpronouncable2(Translator *tr, char *word)
+{//===================================================
int c;
- int vowel_posn=9;
- int index;
- int count;
- int ix;
- int apostrophe=0;
-
- static unsigned char initials_bitmap[86] = {
- 0x00, 0x00, 0x00, 0x00, 0x22, 0x08, 0x00, 0x88, // 0
- 0x20, 0x24, 0x20, 0x80, 0x10, 0x00, 0x00, 0x00,
- 0x00, 0x28, 0x08, 0x00, 0x88, 0x22, 0x04, 0x00, // 16
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x88, 0x22, 0x04, 0x00, 0x02, 0x00, 0x04, // 32
- 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x28, 0x8a, 0x03, 0x00, 0x00, 0x40, 0x00, // 48
- 0x02, 0x00, 0x41, 0xca, 0xbb, 0x06, 0x20, 0x80,
- 0x91, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, // 64
- 0x08, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, };
-
-
- // words which we pass through to the dictionary, even though they look unpronouncable
- static const char *exceptions[] = {
- "'s ", "st ","nd ","rd ","th ",NULL };
-
- if((*word == ' ') || (*word == 0))
- return(0);
-
- for(ix=0; exceptions[ix] != NULL; ix++)
- {
- // Seemingly uncpronouncable words, but to be looked in the dictionary rules instead
- if(memcmp(word,exceptions[ix],3)==0)
- return(0);
- }
-
- index=0;
- count=0;
- for(;;)
- {
- index += utf8_in(&c,&word[index]);
- count++;
-
- if((c==0) || (c==' '))
- break;
-
- if(IsVowel(tr, c) || (c == 'y'))
- {
- vowel_posn = count;
- break;
- }
-
- if(c == '\'')
- apostrophe = 1;
- else
- if(!IsAlpha(c))
- return(0); // letter (not vowel) outside Latin character range or apostrophe, abort test
- }
- if((vowel_posn > 5) || ((word[0]!='s') && (vowel_posn > 4)))
- return(1); // no vowel, or no vowel in first four letters
-
- /* there is at least one vowel, is the initial letter combination valid ? */
-
- if(vowel_posn < 3)
- return(0); /* vowel in first two letters, OK */
-
- if(apostrophe)
- return(0); // first two letters not a-z, abort test
-
- index = (word[0]-'a') * 26 + (word[1]-'a');
- if(initials_bitmap[index >> 3] & (1L << (index & 7)))
- return(0);
- else
- return(1); /****/
-} /* end of Unpronounceable */
-
-
+ int end_flags;
+ char ph_buf[N_WORD_PHONEMES];
+
+ ph_buf[0] = 0;
+ c = word[-1];
+ word[-1] = ' '; // ensure there is a space before the "word"
+ end_flags = TranslateRules(tr, word, ph_buf, sizeof(ph_buf), NULL, FLAG_UNPRON_TEST, NULL);
+ word[-1] = c;
+ if((end_flags == 0) || (end_flags & SUFX_UNPRON))
+ return(1);
+ return(0);
+}
-int Unpronouncable(Translator *tr, char *word)
-{//===========================================
-/* Determines whether a word in 'unpronouncable', i.e. whether it should
- be spoken as individual letters.
+int Unpronouncable(Translator *tr, char *word, int posn)
+{//=====================================================
+ /* Determines whether a word in 'unpronouncable', i.e. whether it should
+ be spoken as individual letters.
- This function may be language specific. This is a generic version.
-*/
+ This function may be language specific. This is a generic version.
+ */
int c;
int c1=0;
int vowel_posn=9;
int index;
int count;
- int apostrophe=0;
-
- if(tr->translator_name == L('e','n'))
- {
- return(Unpronouncable_en(tr,word));
- }
+ ALPHABET *alphabet;
utf8_in(&c,word);
if((tr->letter_bits_offset > 0) && (c < 0x241))
@@ -786,10 +912,16 @@ int Unpronouncable(Translator *tr, char *word)
return(0); // so we can re-translate the word as English
}
+ if(((alphabet = AlphabetFromChar(c)) != NULL) && (alphabet->offset != tr->letter_bits_offset))
+ {
+ // Character is not in our alphabet
+ return(0);
+ }
+
if(tr->langopts.param[LOPT_UNPRONOUNCABLE] == 1)
return(0);
- if((*word == ' ') || (*word == 0))
+ if(((c = *word) == ' ') || (c == 0) || (c == '\''))
return(0);
index = 0;
@@ -800,9 +932,18 @@ int Unpronouncable(Translator *tr, char *word)
if((c==0) || (c==' '))
break;
+ if((c=='\'') && ((count > 1) || (posn > 0)))
+ break; // "tv'" but not "l'"
+
if(count==0)
c1 = c;
- count++;
+
+ if((c == '\'') && (tr->langopts.param[LOPT_UNPRONOUNCABLE] == 3))
+ {
+ // don't count apostrophe
+ }
+ else
+ count++;
if(IsVowel(tr, c))
{
@@ -810,23 +951,23 @@ int Unpronouncable(Translator *tr, char *word)
break;
}
- if(c == '\'')
- apostrophe = 1;
- else
- if(!iswalpha(c))
- return(0); // letter (not vowel) outside a-z range or apostrophe, abort test
+ if((c != '\'') && !iswalpha2(c))
+ return(0);
}
- if((vowel_posn < 9) && (tr->langopts.param[LOPT_UNPRONOUNCABLE] == 2))
- return(0); // option means allow any word with a vowel
+ if((vowel_posn > 2) && (tr->langopts.param[LOPT_UNPRONOUNCABLE] == 2))
+ {
+ // Lookup unpronounable rules in *_rules
+ return(Unpronouncable2(tr, word));
+ }
if(c1 == tr->langopts.param[LOPT_UNPRONOUNCABLE])
vowel_posn--; // disregard this as the initial letter when counting
if(vowel_posn > (tr->langopts.max_initial_consonants+1))
- return(1); // no vowel, or no vowel in first four letters
+ return(1); // no vowel, or no vowel in first few letters
-return(0);
+ return(0);
} /* end of Unpronounceable */
@@ -839,26 +980,26 @@ return(0);
-static int GetVowelStress(Translator *tr, unsigned char *phonemes, unsigned char *vowel_stress, int *vowel_count, int *stressed_syllable, int control)
-{//====================================================================================================================================================
+static int GetVowelStress(Translator *tr, unsigned char *phonemes, signed char *vowel_stress, int *vowel_count, int *stressed_syllable, int control)
+{//=================================================================================================================================================
// control = 1, set stress to 1 for forced unstressed vowels
unsigned char phcode;
PHONEME_TAB *ph;
unsigned char *ph_out = phonemes;
int count = 1;
- int max_stress = 0;
+ int max_stress = -1;
int ix;
int j;
- int stress = 0;
+ int stress = -1;
int primary_posn = 0;
- vowel_stress[0] = 0;
+ vowel_stress[0] = 1;
while(((phcode = *phonemes++) != 0) && (count < (N_WORD_PHONEMES/2)-1))
{
if((ph = phoneme_tab[phcode]) == NULL)
continue;
- if((ph->type == phSTRESS) && (ph->spect == 0))
+ if((ph->type == phSTRESS) && (ph->program == 0))
{
/* stress marker, use this for the following vowel */
@@ -868,7 +1009,7 @@ static int GetVowelStress(Translator *tr, unsigned char *phonemes, unsigned char
j = count - 1;
while((j > 0) && (*stressed_syllable == 0) && (vowel_stress[j] < 4))
{
- if(vowel_stress[j] != 1)
+ if((vowel_stress[j] != 0) && (vowel_stress[j] != 1))
{
// don't promote a phoneme which must be unstressed
vowel_stress[j] = 4;
@@ -878,7 +1019,7 @@ static int GetVowelStress(Translator *tr, unsigned char *phonemes, unsigned char
max_stress = 4;
primary_posn = j;
}
-
+
/* reduce any preceding primary stress markers */
for(ix=1; ix<j; ix++)
{
@@ -912,14 +1053,13 @@ static int GetVowelStress(Translator *tr, unsigned char *phonemes, unsigned char
max_stress = stress;
}
- if((stress == 0) && (control & 1) && (ph->phflags & phUNSTRESSED))
+ if((stress < 0) && (control & 1) && (ph->phflags & phUNSTRESSED))
vowel_stress[count] = 1; /* weak vowel, must be unstressed */
count++;
- stress = 0;
+ stress = -1;
}
- else
- if(phcode == phonSYLLABIC)
+ else if(phcode == phonSYLLABIC)
{
// previous consonant phoneme is syllablic
vowel_stress[count] = (char)stress;
@@ -929,7 +1069,7 @@ static int GetVowelStress(Translator *tr, unsigned char *phonemes, unsigned char
*ph_out++ = phcode;
}
- vowel_stress[count] = 0;
+ vowel_stress[count] = 1;
*ph_out = 0;
/* has the position of the primary stress been specified by $1, $2, etc? */
@@ -950,8 +1090,8 @@ static int GetVowelStress(Translator *tr, unsigned char *phonemes, unsigned char
{
if(vowel_stress[ix] == 4)
{
- if(tr->langopts.stress_flags & 0x20000)
- vowel_stress[ix] = 0;
+ if(tr->langopts.stress_flags & S_PRIORITY_STRESS)
+ vowel_stress[ix] = 1;
else
vowel_stress[ix] = 3;
}
@@ -972,8 +1112,9 @@ static int GetVowelStress(Translator *tr, unsigned char *phonemes, unsigned char
-static char stress_phonemes[] = {phonSTRESS_U, phonSTRESS_D, phonSTRESS_2, phonSTRESS_3,
- phonSTRESS_P, phonSTRESS_P2, phonSTRESS_TONIC};
+static char stress_phonemes[] = {phonSTRESS_D, phonSTRESS_U, phonSTRESS_2, phonSTRESS_3,
+ phonSTRESS_P, phonSTRESS_P2, phonSTRESS_TONIC
+ };
void ChangeWordStress(Translator *tr, char *word, int new_stress)
@@ -984,7 +1125,7 @@ void ChangeWordStress(Translator *tr, char *word, int new_stress)
int vowel_count; // num of vowels + 1
int stressed_syllable=0; // position of stressed syllable
unsigned char phonetic[N_WORD_PHONEMES];
- unsigned char vowel_stress[N_WORD_PHONEMES/2];
+ signed char vowel_stress[N_WORD_PHONEMES/2];
strcpy((char *)phonetic,word);
max_stress = GetVowelStress(tr, phonetic, vowel_stress, &vowel_count, &stressed_syllable, 0);
@@ -1018,8 +1159,8 @@ void ChangeWordStress(Translator *tr, char *word, int new_stress)
{
if((phoneme_tab[*p]->type == phVOWEL) && !(phoneme_tab[*p]->phflags & phNONSYLLABIC))
{
- if(vowel_stress[ix] != 0)
- *word++ = stress_phonemes[vowel_stress[ix]];
+ if((vowel_stress[ix] == 0) || (vowel_stress[ix] > 1))
+ *word++ = stress_phonemes[(unsigned char)vowel_stress[ix]];
ix++;
}
@@ -1030,17 +1171,19 @@ void ChangeWordStress(Translator *tr, char *word, int new_stress)
-void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags, int tonic, int prev_stress)
-{//=========================================================================================================
+void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags, int tonic, int control)
+{//=====================================================================================================
/* Guess stress pattern of word. This is language specific
+ 'output' is used for input and output
+
'dictionary_flags' has bits 0-3 position of stressed vowel (if > 0)
- or unstressed (if == 7) or syllables 1 and 2 (if == 6)
- bits 8... dictionary flags
+ or unstressed (if == 7) or syllables 1 and 2 (if == 6)
+ bits 8... dictionary flags
If 'tonic' is set (>= 0), replace highest stress by this value.
- Parameter used for input and output
+ control: bit 0 This is an individual symbol, not a word
*/
unsigned char phcode;
@@ -1059,13 +1202,15 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
int final_ph;
int final_ph2;
int mnem;
- int mnem2;
- int post_tonic;
+// int post_tonic; // currently not used
int opt_length;
int done;
int stressflags;
+ int dflags = 0;
+ int first_primary;
+ int long_vowel;
- unsigned char vowel_stress[N_WORD_PHONEMES/2];
+ signed char vowel_stress[N_WORD_PHONEMES/2];
char syllable_weight[N_WORD_PHONEMES/2];
char vowel_length[N_WORD_PHONEMES/2];
unsigned char phonetic[N_WORD_PHONEMES];
@@ -1082,6 +1227,9 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
stressflags = tr->langopts.stress_flags;
+ if(dictionary_flags != NULL)
+ dflags = dictionary_flags[0];
+
/* copy input string into internal buffer */
for(ix=0; ix<N_WORD_PHONEMES; ix++)
{
@@ -1098,22 +1246,19 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
max_output = output + (N_WORD_PHONEMES-3); /* check for overrun */
- // any stress position marked in the xx_list dictionary ?
- stressed_syllable = (*dictionary_flags) & 0x7;
- if((*dictionary_flags) & 0x8)
+ // any stress position marked in the xx_list dictionary ?
+ stressed_syllable = dflags & 0x7;
+ if(dflags & 0x8)
{
// this indicates a word without a primary stress
- stressed_syllable = (*dictionary_flags) & 0x3;
+ stressed_syllable = dflags & 0x3;
unstressed_word = 1;
}
max_stress = GetVowelStress(tr, phonetic, vowel_stress, &vowel_count, &stressed_syllable, 1);
-
- if((max_stress == 0) && (tr->langopts.stress_flags & 1) && (vowel_count == 2))
+ if((max_stress < 0) && dictionary_flags)
{
- // option: don't stress monosyllables except at end-of-clause
- vowel_stress[1] = 1;
- (*dictionary_flags) |= FLAG_STRESS_END2;
+ max_stress = 0;
}
// heavy or light syllables
@@ -1146,12 +1291,13 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
ix++;
}
}
-
+
+
switch(tr->langopts.stress_rule)
{
case 8:
- // stress on first syllable, unless it is a light syllable
- if(syllable_weight[1] > 0)
+ // stress on first syllable, unless it is a light syllable followed by a heavy syllable
+ if((syllable_weight[1] > 0) || (syllable_weight[2] == 0))
break;
// else drop through to case 1
case 1:
@@ -1167,6 +1313,17 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
}
break;
+ case 10: // penultimate, but final if only 1 or 2 syllables
+ if(stressed_syllable == 0)
+ {
+ if(vowel_count < 4)
+ {
+ vowel_stress[vowel_count - 1] = 4;
+ max_stress = 4;
+ break;
+ }
+ }
+ // drop through to next case
case 2:
// a language with stress on penultimate vowel
@@ -1179,33 +1336,40 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
{
stressed_syllable = vowel_count - 2;
- if(stressflags & 0x300)
+ if(stressflags & (S_FINAL_SPANISH | S_FINAL_STRESS_C))
{
// LANG=Spanish, stress on last vowel if the word ends in a consonant other than 'n' or 's'
if(phoneme_tab[final_ph]->type != phVOWEL)
{
- if(stressflags & 0x100)
+ if(stressflags & S_FINAL_STRESS_C)
{
stressed_syllable = vowel_count - 1;
}
else
{
mnem = phoneme_tab[final_ph]->mnemonic;
- mnem2 = phoneme_tab[final_ph2]->mnemonic;
- if((mnem == 's') && (mnem2 == 'n'))
+ if(tr->translator_name == L('a','n'))
{
- // -ns stress remains on penultimate syllable
+ if(((mnem != 's') && (mnem !='n')) || phoneme_tab[final_ph2]->type != phVOWEL)
+ stressed_syllable = vowel_count - 1; // stress on last syllable
}
else
- if(((mnem != 'n') && (mnem != 's')) || (phoneme_tab[final_ph2]->type != phVOWEL))
{
- stressed_syllable = vowel_count - 1;
+ if((mnem == 's') && (phoneme_tab[final_ph2]->type == phNASAL))
+ {
+ // -ns stress remains on penultimate syllable
+ }
+ else if(((phoneme_tab[final_ph]->type != phNASAL) && (mnem != 's')) || (phoneme_tab[final_ph2]->type != phVOWEL))
+ {
+ stressed_syllable = vowel_count - 1;
+ }
}
}
}
}
- if(stressflags & 0x80000)
+
+ if(stressflags & S_FINAL_LONG)
{
// stress on last syllable if it has a long vowel, but previous syllable has a short vowel
if(vowel_length[vowel_count - 1] > vowel_length[vowel_count - 2])
@@ -1214,7 +1378,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
}
}
- if(vowel_stress[stressed_syllable] == 1)
+ if((vowel_stress[stressed_syllable] == 0) || (vowel_stress[stressed_syllable] == 1))
{
// but this vowel is explicitly marked as unstressed
if(stressed_syllable > 1)
@@ -1229,7 +1393,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
}
// only set the stress if it's not already marked explicitly
- if(vowel_stress[stressed_syllable] == 0)
+ if(vowel_stress[stressed_syllable] < 0)
{
// don't stress if next and prev syllables are stressed
if((vowel_stress[stressed_syllable-1] < 4) || (vowel_stress[stressed_syllable+1] < 4))
@@ -1238,24 +1402,23 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
}
break;
- case 3:
+ case 3:
// stress on last vowel
if(stressed_syllable == 0)
{
/* no explicit stress - stress the final vowel */
stressed_syllable = vowel_count - 1;
- if(max_stress == 0)
+
+ while(stressed_syllable > 0)
{
- while(stressed_syllable > 0)
+ // find the last vowel which is not unstressed
+ if(vowel_stress[stressed_syllable] < 0)
{
- if(vowel_stress[stressed_syllable] == 0)
- {
- vowel_stress[stressed_syllable] = 4;
- break;
- }
- else
- stressed_syllable--;
+ vowel_stress[stressed_syllable] = 4;
+ break;
}
+ else
+ stressed_syllable--;
}
max_stress = 4;
}
@@ -1290,8 +1453,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
{
if(phoneme_tab[final_ph]->type == phVOWEL)
stressed_syllable = guess_ru_v[vowel_count];
- else
- if(phoneme_tab[final_ph]->type == phSTOP)
+ else if(phoneme_tab[final_ph]->type == phSTOP)
stressed_syllable = guess_ru_t[vowel_count];
else
stressed_syllable = guess_ru[vowel_count];
@@ -1306,17 +1468,17 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
{
int wt;
int max_weight = -1;
- int prev_stressed;
+// int prev_stressed;
// find the heaviest syllable, excluding the final syllable
for(ix = 1; ix < (vowel_count-1); ix++)
{
- if(vowel_stress[ix] == 0)
+ if(vowel_stress[ix] < 0)
{
if((wt = syllable_weight[ix]) >= max_weight)
{
max_weight = wt;
- prev_stressed = stressed_syllable;
+// prev_stressed = stressed_syllable;
stressed_syllable = ix;
}
}
@@ -1327,8 +1489,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
// the only double=heavy syllable is the final syllable, so stress this
stressed_syllable = vowel_count-1;
}
- else
- if(max_weight <= 0)
+ else if(max_weight <= 0)
{
// all syllables, exclusing the last, are light. Stress the first syllable
stressed_syllable = 1;
@@ -1339,7 +1500,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
}
break;
- case 7: // LANG=tr, the last syllable for any vowel markes explicitly as unstressed
+ case 7: // LANG=tr, the last syllable for any vowel marked explicitly as unstressed
if(stressed_syllable == 0)
{
stressed_syllable = vowel_count - 1;
@@ -1359,10 +1520,54 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
case 9: // mark all as stressed
for(ix=1; ix<vowel_count; ix++)
{
- if(vowel_stress[ix] == 0)
+ if(vowel_stress[ix] < 0)
vowel_stress[ix] = 4;
}
break;
+
+ case 12: // LANG=kl (Greenlandic)
+ long_vowel = 0;
+ for(ix=1; ix < vowel_count; ix++)
+ {
+ if(vowel_stress[ix] == 4)
+ vowel_stress[ix] = 3; // change marked stress (consonant clusters) to secondary (except the last)
+
+ if(vowel_length[ix] > 0)
+ {
+ long_vowel = ix;
+ vowel_stress[ix] = 3; // give secondary stress to all long vowels
+ }
+ }
+
+ // 'stressed_syllable' gives the last marked stress
+ if(stressed_syllable == 0)
+ {
+ // no marked stress, choose the last long vowel
+ if(long_vowel > 0)
+ stressed_syllable = long_vowel;
+ else
+ {
+ // no long vowels or consonant clusters
+ if(vowel_count > 5)
+ stressed_syllable = vowel_count - 3; // more than 4 syllables
+ else
+ stressed_syllable = vowel_count - 1;
+ }
+ }
+ vowel_stress[stressed_syllable] = 4;
+ max_stress = 4;
+ break;
+
+ case 13: // LANG=ml, 1st unless 1st vowel is short and 2nd is long
+ if(stressed_syllable == 0)
+ {
+ stressed_syllable = 1;
+ if((vowel_length[1] == 0) && (vowel_count > 2) && (vowel_length[2] > 0))
+ stressed_syllable = 2;
+ vowel_stress[stressed_syllable] = 4;
+ max_stress = 4;
+ }
+ break;
}
/* now guess the complete stress pattern */
@@ -1372,50 +1577,51 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
stress = 3;
- if((stressflags & 0x1000) && (vowel_count == 2))
+ if(unstressed_word == 0)
{
- // Two syllable word, if one syllable has primary stress, then give the other secondary stress
- if(vowel_stress[1] == 4)
- vowel_stress[2] = 3;
- if(vowel_stress[2] == 4)
- vowel_stress[1] = 3;
- }
-#if deleted
- if((stressflags & 0x2000) && (vowel_stress[1] == 0))
- {
- // If there is only one syllable before the primary stress, give it a secondary stress
- if((vowel_count > 2) && (vowel_stress[2] >= 4))
+ if((stressflags & S_2_SYL_2) && (vowel_count == 3))
+ {
+ // Two syllable word, if one syllable has primary stress, then give the other secondary stress
+ if(vowel_stress[1] == 4)
+ vowel_stress[2] = 3;
+ if(vowel_stress[2] == 4)
+ vowel_stress[1] = 3;
+ }
+
+ if((stressflags & S_INITIAL_2) && (vowel_stress[1] < 0))
{
- vowel_stress[1] = 3;
+ // If there is only one syllable before the primary stress, give it a secondary stress
+ if((vowel_count > 3) && (vowel_stress[2] >= 4))
+ {
+ vowel_stress[1] = 3;
+ }
}
}
-#endif
done = 0;
+ first_primary = 0;
for(v=1; v<vowel_count; v++)
{
- if(vowel_stress[v] == 0)
+ if(vowel_stress[v] < 0)
{
- if((stressflags & 0x10) && (stress < 4) && (v == vowel_count-1))
+ if((stressflags & S_FINAL_NO_2) && (stress < 4) && (v == vowel_count-1))
{
// flag: don't give secondary stress to final vowel
}
- else
- if((stressflags & 0x8000) && (done == 0))
+ else if((stressflags & 0x8000) && (done == 0))
{
vowel_stress[v] = (char)stress;
done =1;
stress = 3; /* use secondary stress for remaining syllables */
}
- else
- if((vowel_stress[v-1] <= 1) && ((vowel_stress[v+1] <= 1) || ((stress == 4) && (vowel_stress[v+1] <= 2))))
+ else if((vowel_stress[v-1] <= 1) && ((vowel_stress[v+1] <= 1) || ((stress == 4) && (vowel_stress[v+1] <= 2))))
{
/* trochaic: give stress to vowel surrounded by unstressed vowels */
- if((stress == 3) && (stressflags & 0x20))
+ if((stress == 3) && (stressflags & S_NO_AUTO_2))
continue; // don't use secondary stress
- if((v > 1) && (stressflags & 0x40) && (syllable_weight[v]==0) && (syllable_weight[v+1]>0))
+ if((v > 1) && (stressflags & S_2_TO_HEAVY) && (syllable_weight[v]==0) && (syllable_weight[v+1]>0))
{
// don't put secondary stress on a light syllable which is followed by a heavy syllable
continue;
@@ -1428,6 +1634,17 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
stress = 3; /* use secondary stress for remaining syllables */
}
}
+
+ if(vowel_stress[v] >= 4)
+ {
+ if(first_primary == 0)
+ first_primary = v;
+ else if(stressflags & S_FIRST_PRIMARY)
+ {
+ // reduce primary stresses after the first to secondary
+ vowel_stress[v] = 3;
+ }
+ }
}
if((unstressed_word) && (tonic < 0))
@@ -1464,11 +1681,14 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
p = phonetic;
v = 1;
- if((ph = phoneme_tab[*p]) != NULL)
+ if(!(control & 1) && ((ph = phoneme_tab[*p]) != NULL))
{
- if(ph->type == phSTRESS)
- ph = phoneme_tab[p[1]];
+ while((ph->type == phSTRESS) || (*p == phonEND_WORD))
+ {
+ p++;
+ ph = phoneme_tab[p[0]];
+ }
#ifdef deleted
int gap = tr->langopts.word_gap & 0x700;
@@ -1479,23 +1699,23 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
}
else
#endif
- if((tr->langopts.vowel_pause & 0x30) && (ph->type == phVOWEL))
- {
- // word starts with a vowel
-
- if((tr->langopts.vowel_pause & 0x20) && (vowel_stress[1] >= 4))
+ if((tr->langopts.vowel_pause & 0x30) && (ph->type == phVOWEL))
{
+ // word starts with a vowel
+
+ if((tr->langopts.vowel_pause & 0x20) && (vowel_stress[1] >= 4))
+ {
*output++ = phonPAUSE_NOLINK; // not to be replaced by link
+ }
+ else
+ {
+ *output++ = phonPAUSE_VSHORT; // break, but no pause
+ }
}
- else
- {
- *output++ = phonPAUSE_VSHORT; // break, but no pause
- }
- }
}
p = phonetic;
- post_tonic = 0;
+// post_tonic = 0;
while(((phcode = *p++) != 0) && (output < max_output))
{
if((ph = phoneme_tab[phcode]) == NULL)
@@ -1508,49 +1728,46 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
{
tr->prev_last_stress = 0;
}
- else
- if(((ph->type == phVOWEL) && !(ph->phflags & phNONSYLLABIC)) || (*p == phonSYLLABIC))
+ else if(((ph->type == phVOWEL) && !(ph->phflags & phNONSYLLABIC)) || (*p == phonSYLLABIC))
{
// a vowel, or a consonant followed by a syllabic consonant marker
v_stress = vowel_stress[v];
tr->prev_last_stress = v_stress;
- if(vowel_stress[v-1] >= max_stress)
- post_tonic = 1;
+// if(vowel_stress[v-1] >= max_stress)
+// post_tonic = 1;
if(v_stress <= 1)
{
- if((v > 1) && (max_stress >= 4) && (stressflags & 4) && (v == (vowel_count-1)))
+ if((v > 1) && (max_stress >= 2) && (stressflags & S_FINAL_DIM) && (v == (vowel_count-1)))
{
// option: mark unstressed final syllable as diminished
- v_stress = 1;
+ v_stress = 0;
}
- else
- if((stressflags & 2) || (v == 1) || (v == (vowel_count-1)))
+ else if((stressflags & S_NO_DIM) || (v == 1) || (v == (vowel_count-1)))
{
// first or last syllable, or option 'don't set diminished stress'
- v_stress = 0;
+ v_stress = 1;
}
- else
- if((v == (vowel_count-2)) && (vowel_stress[vowel_count-1] <= 1))
+ else if((v == (vowel_count-2)) && (vowel_stress[vowel_count-1] <= 1))
{
// penultimate syllable, followed by an unstressed final syllable
- v_stress = 0;
+ v_stress = 1;
}
else
{
// unstressed syllable within a word
- if((vowel_stress[v-1] != 1) || ((stressflags & 0x10000) == 0))
+ if((vowel_stress[v-1] < 0) || ((stressflags & S_MID_DIM) == 0))
{
- v_stress = 1; /* change from 0 (unstressed) to 1 (diminished stress) */
+ v_stress = 0; /* change to 0 (diminished stress) */
vowel_stress[v] = v_stress;
}
}
}
- if(v_stress > 0)
- *output++ = stress_phonemes[v_stress]; // mark stress of all vowels except 0 (unstressed)
+ if((v_stress == 0) || (v_stress > 1))
+ *output++ = stress_phonemes[v_stress]; // mark stress of all vowels except 1 (unstressed)
if(vowel_stress[v] > max_stress)
@@ -1569,8 +1786,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
if(v != max_stress_posn)
shorten = 1;
}
- else
- if(v_stress < 4)
+ else if(v_stress < 4)
{
// only allow lengthen indicator if stress >= 4.
shorten = 1;
@@ -1598,6 +1814,7 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
}
*output++ = 0;
+ return;
} /* end of SetWordStress */
@@ -1609,153 +1826,6 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags,
//=============================================================================================
-#ifdef LOG_TRANSLATE
-static char *DecodeRule(const char *group, char *rule)
-{//==================================================
-/* Convert compiled match template to ascii */
-
- unsigned char rb;
- unsigned char c;
- char *p;
- int ix;
- int match_type;
- int finished=0;
- int value;
- int linenum=0;
- int flags;
- int suffix_char;
- int condition_num=0;
- char buf[60];
- char buf_pre[60];
- char suffix[20];
- static char output[60];
-
- static char symbols[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',
- '@','&','%','+','#','S','D','Z','A','L',' ',' ',' ',' ',' ','N','K','V',' ','T','X','?','W'};
-
- static char symbols_lg[] = {'A','B','C','H','F','G','Y'};
-
- match_type = 0;
- buf_pre[0] = 0;
- strcpy(buf,group);
- p = &buf[strlen(buf)];
- while(!finished)
- {
- rb = *rule++;
-
- if(rb <= RULE_LINENUM)
- {
- switch(rb)
- {
- case 0:
- case RULE_PHONEMES:
- finished=1;
- break;
- case RULE_PRE:
- match_type = RULE_PRE;
- *p = 0;
- p = buf_pre;
- break;
- case RULE_POST:
- match_type = RULE_POST;
- *p = 0;
- strcat(buf," (");
- p = &buf[strlen(buf)];
- break;
- case RULE_PH_COMMON:
- break;
- case RULE_CONDITION:
- /* conditional rule, next byte gives condition number */
- condition_num = *rule++;
- break;
- case RULE_LINENUM:
- value = (rule[1] & 0xff) - 1;
- linenum = (rule[0] & 0xff) - 1 + (value * 255);
- rule+=2;
- break;
- }
- continue;
- }
-
- if(rb == RULE_ENDING)
- {
- static const char *flag_chars = "ei vtfq t";
- flags = ((rule[0] & 0x7f)<< 8) + (rule[1] & 0x7f);
- suffix_char = 'S';
- if(flags & (SUFX_P >> 8))
- suffix_char = 'P';
- sprintf(suffix,"%c%d",suffix_char,rule[2] & 0x7f);
- rule += 3;
- for(ix=0;ix<9;ix++)
- {
- if(flags & 1)
- sprintf(&suffix[strlen(suffix)],"%c",flag_chars[ix]);
- flags = (flags >> 1);
- }
- strcpy(p,suffix);
- p += strlen(suffix);
- c = ' ';
- }
- else
- if(rb == RULE_LETTERGP)
- {
- c = symbols_lg[*rule++ - 'A'];
- }
- else
- if(rb == RULE_LETTERGP2)
- {
- value = *rule++ - 'A';
- p[0] = 'L';
- p[1] = (value / 10) + '0';
- c = (value % 10) + '0';
-
- if(match_type == RULE_PRE)
- {
- p[0] = c;
- c = 'L';
- }
- p+=2;
- }
- else
- if(rb <= RULE_LAST_RULE)
- c = symbols[rb];
- else
- if(rb == RULE_SPACE)
- c = '_';
- else
- c = rb;
- *p++ = c;
- }
- *p = 0;
-
- p = output;
- if(linenum > 0)
- {
- sprintf(p,"%5d:\t",linenum);
- p += 7;
- }
- if(condition_num > 0)
- {
- sprintf(p,"?%d ",condition_num);
- p = &p[strlen(p)];
- }
- if((ix = strlen(buf_pre)) > 0)
- {
- while(--ix >= 0)
- *p++ = buf_pre[ix];
- *p++ = ')';
- *p++ = ' ';
- }
- *p = 0;
- strcat(p,buf);
- ix = strlen(output);
- while(ix < 8)
- output[ix++]=' ';
- output[ix]=0;
- return(output);
-} /* end of decode_match */
-#endif
-
void AppendPhonemes(Translator *tr, char *string, int size, const char *ph)
@@ -1763,7 +1833,7 @@ void AppendPhonemes(Translator *tr, char *string, int size, const char *ph)
/* Add new phoneme string "ph" to "string"
Keeps count of the number of vowel phonemes in the word, and whether these
can be stressed syllables. These values can be used in translation rules
-*/
+*/
const char *p;
unsigned char c;
int unstress_mark;
@@ -1792,7 +1862,7 @@ void AppendPhonemes(Translator *tr, char *string, int size, const char *ph)
if(phoneme_tab[c]->type == phVOWEL)
{
if(((phoneme_tab[c]->phflags & phUNSTRESSED) == 0) &&
- (unstress_mark == 0))
+ (unstress_mark == 0))
{
tr->word_stressed_count++;
}
@@ -1801,15 +1871,15 @@ void AppendPhonemes(Translator *tr, char *string, int size, const char *ph)
}
}
}
-
+
if(string != NULL)
strcat(string,ph);
} /* end of AppendPhonemes */
-static void MatchRule(Translator *tr, char *word[], const char *group, char *rule, MatchRecord *match_out, int word_flags, int dict_flags)
-{//=======================================================================================================================================
+static void MatchRule(Translator *tr, char *word[], char *word_start, int group_length, char *rule, MatchRecord *match_out, int word_flags, int dict_flags)
+{//========================================================================================================================================================
/* Checks a specified word against dictionary rules.
Returns with phoneme code string, or NULL if no match found.
@@ -1836,11 +1906,12 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
char *rule_start; /* start of current match template */
char *p;
+ int ix;
int match_type; /* left, right, or consume */
int failed;
+ int unpron_ignore;
int consumed; /* number of letters consumed from input */
- int count; /* count through rules in the group */
int syllable_count;
int vowel;
int letter_group;
@@ -1849,17 +1920,21 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
int lg_pts;
int n_bytes;
int add_points;
+ int command;
+ int check_atstart;
+ unsigned int *flags;
MatchRecord match;
static MatchRecord best;
int total_consumed; /* letters consumed for best match */
- int group_length;
unsigned char condition_num;
char *common_phonemes; /* common to a group of entries */
+ char *group_chars;
+ char word_buf[N_WORD_BYTES];
-
+ group_chars = *word;
if(rule == NULL)
{
@@ -1870,7 +1945,6 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
total_consumed = 0;
- count = 0;
common_phonemes = NULL;
match_type = 0;
@@ -1879,27 +1953,27 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
best.end_type = 0;
best.del_fwd = NULL;
- group_length = strlen(group);
-
/* search through dictionary rules */
while(rule[0] != RULE_GROUP_END)
{
+ unpron_ignore = word_flags & FLAG_UNPRON_TEST;
match_type=0;
consumed = 0;
letter = 0;
distance_right= -6; /* used to reduce points for matches further away the current letter */
distance_left= -2;
- count++;
+ check_atstart = 0;
match.points = 1;
match.end_type = 0;
match.del_fwd = NULL;
-
+
pre_ptr = *word;
post_ptr = *word + group_length;
/* work through next rule until end, or until no-match proved */
rule_start = rule;
+
failed = 0;
while(!failed)
{
@@ -1917,6 +1991,8 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
{
if(rb == RULE_CONDITION)
match.phonemes++; // skip over condition number
+ if(rb == RULE_LINENUM)
+ match.phonemes += 2; // skip over line number
}
}
else
@@ -1926,9 +2002,24 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
rule--; // so we are still pointing at the 0
failed=2; // matched OK
break;
+
+ case RULE_PRE_ATSTART: // pre rule with implied 'start of word'
+ check_atstart = 1;
+ unpron_ignore = 0;
+ match_type = RULE_PRE;
+ break;
+
case RULE_PRE:
match_type = RULE_PRE;
+ if(word_flags & FLAG_UNPRON_TEST)
+ {
+ // checking the start of the word for unpronouncable character sequences, only
+ // consider rules which explicitly match the start of a word
+ // Note: Those rules now use RULE_PRE_ATSTART
+ failed = 1;
+ }
break;
+
case RULE_POST:
match_type = RULE_POST;
break;
@@ -1942,7 +2033,7 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
case RULE_CONDITION:
/* conditional rule, next byte gives condition number */
condition_num = *rule++;
-
+
if(condition_num >= 32)
{
// allow the rule only if the condition number is NOT set
@@ -1977,7 +2068,8 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
if((letter == rb) || ((letter==(unsigned char)REPLACED_E) && (rb=='e')))
{
- add_points = 21;
+ if((letter & 0xc0) != 0x80)
+ add_points = 21; // don't add point for non-initial UTF-8 bytes
consumed++;
}
else
@@ -2022,13 +2114,15 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
break;
case RULE_NOTVOWEL:
- if(!IsLetter(tr, letter_w,0))
+ if(IsLetter(tr, letter_w, 0) || ((letter_w == ' ') && (word_flags & FLAG_SUFFIX_VOWEL)))
+ {
+ failed = 1;
+ }
+ else
{
add_points = (20-distance_right);
post_ptr += letter_xbytes;
}
- else
- failed = 1;
break;
case RULE_DIGIT:
@@ -2037,8 +2131,7 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
add_points = (20-distance_right);
post_ptr += letter_xbytes;
}
- else
- if(tr->langopts.tone_numbers)
+ else if(tr->langopts.tone_numbers)
{
// also match if there is no digit
add_points = (20-distance_right);
@@ -2047,9 +2140,9 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
else
failed = 1;
break;
-
+
case RULE_NONALPHA:
- if(!iswalpha(letter_w))
+ if(!iswalpha2(letter_w))
{
add_points = (21-distance_right);
post_ptr += letter_xbytes;
@@ -2065,11 +2158,45 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
failed = 1;
break;
- case RULE_ALT1:
- if(dict_flags & FLAG_ALT_TRANS)
- add_points = 1;
- else
- failed = 1;
+ case RULE_DOLLAR:
+ command = *rule++;
+ if(command == DOLLAR_UNPR)
+ {
+ match.end_type = SUFX_UNPRON; // $unpron
+ }
+ else if(command == DOLLAR_NOPREFIX) // $noprefix
+ {
+ if(word_flags & FLAG_PREFIX_REMOVED)
+ failed = 1; // a prefix has been removed
+ else
+ add_points = 1;
+ }
+ else if((command & 0xf0) == 0x10)
+ {
+ // $w_alt
+ if(dict_flags & (1 << (BITNUM_FLAG_ALT + (command & 0xf))))
+ add_points = 23;
+ else
+ failed = 1;
+ }
+ else if(((command & 0xf0) == 0x20) || (command == DOLLAR_LIST))
+ {
+ // $list or $p_alt
+ // make a copy of the word up to the post-match characters
+ ix = *word - word_start + consumed + group_length + 1;
+ memcpy(word_buf, word_start-1, ix);
+ word_buf[ix] = ' ';
+ word_buf[ix+1] = 0;
+ LookupFlags(tr, &word_buf[1], &flags);
+
+ if((command == DOLLAR_LIST) && (flags[0] & FLAG_FOUND) && !(flags[1] & FLAG_ONLY))
+ add_points = 23;
+ else
+ if(flags[0] & (1 << (BITNUM_FLAG_ALT + (command & 0xf))))
+ add_points = 23;
+ else
+ failed = 1;
+ }
break;
case '-':
@@ -2082,58 +2209,80 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
break;
case RULE_SYLLABLE:
- {
- /* more than specified number of vowel letters to the right */
- char *p = post_ptr + letter_xbytes;
+ {
+ /* more than specified number of vowel letters to the right */
+ char *p = post_ptr + letter_xbytes;
+ int vowel_count=0;
- syllable_count = 1;
- while(*rule == RULE_SYLLABLE)
- {
- rule++;
- syllable_count+=1; /* number of syllables to match */
- }
- vowel = 0;
- while(letter_w != RULE_SPACE)
+ syllable_count = 1;
+ while(*rule == RULE_SYLLABLE)
+ {
+ rule++;
+ syllable_count+=1; /* number of syllables to match */
+ }
+ vowel = 0;
+ while(letter_w != RULE_SPACE)
+ {
+ if((vowel==0) && IsLetter(tr, letter_w,LETTERGP_VOWEL2))
{
- if((vowel==0) && IsLetter(tr, letter_w,LETTERGP_VOWEL2))
- {
- // this is counting vowels which are separated by non-vowels
- syllable_count--;
- }
- vowel = IsLetter(tr, letter_w,LETTERGP_VOWEL2);
- p += utf8_in(&letter_w,p);
+ // this is counting vowels which are separated by non-vowel letters
+ vowel_count++;
}
- if(syllable_count <= 0)
- add_points = (19-distance_right);
- else
- failed = 1;
+ vowel = IsLetter(tr, letter_w,LETTERGP_VOWEL2);
+ p += utf8_in(&letter_w,p);
}
- break;
+ if(syllable_count <= vowel_count)
+ add_points = (18+syllable_count-distance_right);
+ else
+ failed = 1;
+ }
+ break;
case RULE_NOVOWELS:
+ {
+ char *p = post_ptr + letter_xbytes;
+ while(letter_w != RULE_SPACE)
{
- char *p = post_ptr + letter_xbytes;
- while(letter_w != RULE_SPACE)
+ if(IsLetter(tr, letter_w,LETTERGP_VOWEL2))
{
- if(IsLetter(tr, letter_w,LETTERGP_VOWEL2))
- {
- failed = 1;
- break;
- }
- p += utf8_in(&letter_w,p);
+ failed = 1;
+ break;
}
- if(!failed)
- add_points = (19-distance_right);
+ p += utf8_in(&letter_w,p);
}
- break;
+ if(!failed)
+ add_points = (19-distance_right);
+ }
+ break;
+
+ case RULE_SKIPCHARS:
+ {
+ // Used for lang=Tamil, used to match on the next word after an unknown word ending
+ // only look until the end of the word (including the end-of-word marker)
+ // Jx means 'skip characters until x', where 'x' may be '_' for 'end of word'
+ char *p = post_ptr + letter_xbytes;
+ char *p2 = p;
+ int rule_w; // skip characters until this
+ utf8_in(&rule_w,rule);
+ while((letter_w != rule_w) && (letter_w != RULE_SPACE))
+ {
+ p2 = p;
+ p += utf8_in(&letter_w,p);
+ }
+ if(letter_w == rule_w)
+ {
+ post_ptr = p2;
+ }
+ }
+ break;
case RULE_INC_SCORE:
add_points = 20; // force an increase in points
break;
case RULE_DEL_FWD:
- // find the next 'e' in the word and replace by ''
- for(p = *word + group_length; *p != ' '; p++)
+ // find the next 'e' in the word and replace by 'E'
+ for(p = *word + group_length; p < post_ptr; p++)
{
if(*p == 'e')
{
@@ -2144,10 +2293,20 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
break;
case RULE_ENDING:
+ {
+ int end_type;
// next 3 bytes are a (non-zero) ending type. 2 bytes of flags + suffix length
- match.end_type = (rule[0] << 16) + ((rule[1] & 0x7f) << 8) + (rule[2] & 0x7f);
- rule += 3;
- break;
+ end_type = (rule[0] << 16) + ((rule[1] & 0x7f) << 8) + (rule[2] & 0x7f);
+
+ if((tr->word_vowel_count == 0) && !(end_type & SUFX_P) && (tr->langopts.param[LOPT_SUFFIX] & 1))
+ failed = 1; // don't match a suffix rule if there are no previous syllables (needed for lang=tr).
+ else
+ {
+ match.end_type = end_type;
+ rule += 3;
+ }
+ }
+ break;
case RULE_NO_SUFFIX:
if(word_flags & FLAG_SUFFIX_REMOVED)
@@ -2159,10 +2318,11 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
default:
if(letter == rb)
{
- if(letter == RULE_SPACE)
- add_points = (21-distance_right);
- else
+ if((letter & 0xc0) != 0x80)
+ {
+ // not for non-initial UTF-8 bytes
add_points = (21-distance_right);
+ }
}
else
failed = 1;
@@ -2237,7 +2397,7 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
break;
case RULE_NONALPHA:
- if(!iswalpha(letter_w))
+ if(!iswalpha2(letter_w))
{
add_points = (21-distance_right);
pre_ptr -= letter_xbytes;
@@ -2246,6 +2406,28 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
failed = 1;
break;
+ case RULE_DOLLAR:
+ command = *rule++;
+ if((command==DOLLAR_LIST) || ((command & 0xf0) == 0x20))
+ {
+ // $list or $p_alt
+ // make a copy of the word up to the current character
+ ix = *word - word_start + 1;
+ memcpy(word_buf, word_start-1, ix);
+ word_buf[ix] = ' ';
+ word_buf[ix+1] = 0;
+ LookupFlags(tr, &word_buf[1], &flags);
+
+ if((command==DOLLAR_LIST) && (flags[0] & FLAG_FOUND) && !(flags[1] & FLAG_ONLY))
+ add_points = 23;
+ else
+ if(flags[0] & (1 << (BITNUM_FLAG_ALT + (command & 0xf))))
+ add_points = 23;
+ else
+ failed = 1;
+ }
+ break;
+
case RULE_SYLLABLE:
/* more than specified number of vowels to the left */
syllable_count = 1;
@@ -2255,7 +2437,7 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
syllable_count++; /* number of syllables to match */
}
if(syllable_count <= tr->word_vowel_count)
- add_points = (19-distance_left);
+ add_points = (18+syllable_count-distance_left);
else
failed = 1;
break;
@@ -2268,21 +2450,21 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
break;
case RULE_NOVOWELS:
+ {
+ char *p = pre_ptr - letter_xbytes - 1;
+ while(letter_w != RULE_SPACE)
{
- char *p = pre_ptr - letter_xbytes - 1;
- while(letter_w != RULE_SPACE)
+ if(IsLetter(tr, letter_w,LETTERGP_VOWEL2))
{
- if(IsLetter(tr, letter_w,LETTERGP_VOWEL2))
- {
- failed = 1;
- break;
- }
- p -= utf8_in2(&letter_w,p,1);
+ failed = 1;
+ break;
}
- if(!failed)
- add_points = 3;
+ p -= utf8_in2(&letter_w,p,1);
}
- break;
+ if(!failed)
+ add_points = 3;
+ }
+ break;
case RULE_IFVERB:
if(tr->expect_verb)
@@ -2327,7 +2509,13 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
if(letter == RULE_SPACE)
add_points = 4;
else
- add_points = (21-distance_left);
+ {
+ if((letter & 0xc0) != 0x80)
+ {
+ // not for non-initial UTF-8 bytes
+ add_points = (21-distance_left);
+ }
+ }
}
else
failed = 1;
@@ -2340,44 +2528,45 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
match.points += add_points;
}
- if(failed == 2)
+ if((failed == 2) && (unpron_ignore == 0))
{
- /* matched OK, is this better than the last best match ? */
- if(match.points >= best.points)
+ // do we also need to check for 'start of word' ?
+ if((check_atstart==0) || (pre_ptr[-1] == ' '))
{
- memcpy(&best,&match,sizeof(match));
- total_consumed = consumed;
- }
+ if(check_atstart)
+ match.points += 4;
-#ifdef LOG_TRANSLATE
- if((option_phonemes == 2) && (match.points > 0) && ((word_flags & FLAG_NO_TRACE) == 0))
- {
- // show each rule that matches, and it's points score
- int pts;
- char decoded_phonemes[80];
+ /* matched OK, is this better than the last best match ? */
+ if(match.points >= best.points)
+ {
+ memcpy(&best,&match,sizeof(match));
+ total_consumed = consumed;
+ }
- // note: 'count' contains the rule number, if we want to include it
- pts = match.points;
- if(group_length > 1)
- pts += 35; // to account for an extra letter matching
- DecodePhonemes(match.phonemes,decoded_phonemes);
- fprintf(f_trans,"%3d\t%s [%s]\n",pts,DecodeRule(group,rule_start),decoded_phonemes);
+ if((option_phonemes == 2) && (match.points > 0) && ((word_flags & FLAG_NO_TRACE) == 0))
+ {
+ // show each rule that matches, and it's points score
+ int pts;
+ char decoded_phonemes[80];
+
+ pts = match.points;
+ if(group_length > 1)
+ pts += 35; // to account for an extra letter matching
+ DecodePhonemes(match.phonemes,decoded_phonemes);
+ fprintf(f_trans,"%3d\t%s [%s]\n",pts,DecodeRule(group_chars, group_length, rule_start, word_flags), decoded_phonemes);
+ }
}
-#endif
-
}
/* skip phoneme string to reach start of next template */
while(*rule++ != 0);
}
-#ifdef LOG_TRANSLATE
if((option_phonemes == 2) && ((word_flags & FLAG_NO_TRACE)==0))
{
if(group_length <= 1)
fprintf(f_trans,"\n");
}
-#endif
/* advance input data pointer */
total_consumed += group_length;
@@ -2396,13 +2585,12 @@ static void MatchRule(Translator *tr, char *word[], const char *group, char *rul
int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, char *end_phonemes, int word_flags, unsigned int *dict_flags)
{//=====================================================================================================================================
-/* Translate a word bounded by space characters
- Append the result to 'phonemes' and any standard prefix/suffix in 'end_phonemes' */
+ /* Translate a word bounded by space characters
+ Append the result to 'phonemes' and any standard prefix/suffix in 'end_phonemes' */
unsigned char c, c2;
unsigned int c12;
int wc=0;
- int wc_prev;
int wc_bytes;
char *p2; /* copy of p for use in double letter chain match */
int found;
@@ -2414,6 +2602,7 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
int ix;
unsigned int digit_count=0;
char *p;
+ ALPHABET *alphabet;
int dict_flags0=0;
MatchRecord match1;
MatchRecord match2;
@@ -2421,8 +2610,6 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
char word_copy[N_WORD_BYTES];
static const char str_pause[2] = {phonPAUSE_NOLINK,0};
- char group_name[4];
-
if(tr->data_dictrules == NULL)
return(0);
@@ -2439,31 +2626,31 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
word_copy[ix] = 0;
-#ifdef LOG_TRANSLATE
if((option_phonemes == 2) && ((word_flags & FLAG_NO_TRACE)==0))
{
char wordbuf[120];
- int ix;
+ unsigned int ix;
- for(ix=0; ((c = p_start[ix]) != ' ') && (c != 0); ix++)
+ for(ix=0; ((c = p_start[ix]) != ' ') && (c != 0) && (ix < (sizeof(wordbuf)-1)); ix++)
{
wordbuf[ix] = c;
}
wordbuf[ix] = 0;
- fprintf(f_trans,"Translate '%s'\n",wordbuf);
+ if(word_flags & FLAG_UNPRON_TEST)
+ fprintf(f_trans,"Unpronouncable? '%s'\n",wordbuf);
+ else
+ fprintf(f_trans,"Translate '%s'\n",wordbuf);
}
-#endif
p = p_start;
tr->word_vowel_count = 0;
tr->word_stressed_count = 0;
-
+
if(end_phonemes != NULL)
end_phonemes[0] = 0;
-
+
while(((c = *p) != ' ') && (c != 0))
{
- wc_prev = wc;
wc_bytes = utf8_in(&wc,p);
if(IsAlpha(wc))
any_alpha++;
@@ -2472,14 +2659,14 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
if(IsDigit(wc) && ((tr->langopts.tone_numbers == 0) || !any_alpha))
{
// lookup the number in *_list not *_rules
- char string[8];
- char buf[40];
+ char string[8];
+ char buf[40];
string[0] = '_';
memcpy(&string[1],p,wc_bytes);
string[1+wc_bytes] = 0;
Lookup(tr, string,buf);
if(++digit_count >= 2)
- {
+ {
strcat(buf,str_pause);
digit_count=0;
}
@@ -2491,13 +2678,22 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
{
digit_count = 0;
found = 0;
-
- if(n > 0)
+
+ if(((ix = wc - tr->letter_bits_offset) >= 0) && (ix < 128))
+ {
+ if(tr->groups3[ix] != NULL)
+ {
+ MatchRule(tr, &p, p_start, wc_bytes, tr->groups3[ix], &match1, word_flags, dict_flags0);
+ found = 1;
+ }
+ }
+
+ if(!found && (n > 0))
{
/* there are some 2 byte chains for this initial letter */
c2 = p[1];
c12 = c + (c2 << 8); /* 2 characters */
-
+
g1 = tr->groups2_start[c];
for(g=g1; g < (g1+n); g++)
{
@@ -2505,17 +2701,13 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
{
found = 1;
- group_name[0] = c;
- group_name[1] = c2;
- group_name[2] = 0;
p2 = p;
- MatchRule(tr, &p2, group_name, tr->groups2[g], &match2, word_flags, dict_flags0);
+ MatchRule(tr, &p2, p_start, 2, tr->groups2[g], &match2, word_flags, dict_flags0);
if(match2.points > 0)
match2.points += 35; /* to acount for 2 letters matching */
/* now see whether single letter chain gives a better match ? */
- group_name[1] = 0;
- MatchRule(tr, &p, group_name, tr->groups1[c], &match1, word_flags, dict_flags0);
+ MatchRule(tr, &p, p_start, 1, tr->groups1[c], &match1, word_flags, dict_flags0);
if(match2.points >= match1.points)
{
@@ -2526,19 +2718,16 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
}
}
}
-
+
if(!found)
{
/* alphabetic, single letter chain */
- group_name[0] = c;
- group_name[1] = 0;
-
if(tr->groups1[c] != NULL)
- MatchRule(tr, &p, group_name, tr->groups1[c], &match1, word_flags, dict_flags0);
+ MatchRule(tr, &p, p_start, 1, tr->groups1[c], &match1, word_flags, dict_flags0);
else
{
// no group for this letter, use default group
- MatchRule(tr, &p, "", tr->groups1[0], &match1, word_flags, dict_flags0);
+ MatchRule(tr, &p, p_start, 0, tr->groups1[0], &match1, word_flags, dict_flags0);
if((match1.points == 0) && ((option_sayas & 0x10) == 0))
{
@@ -2547,7 +2736,7 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
if(tr->letter_bits_offset > 0)
{
// not a Latin alphabet, switch to the default Latin alphabet language
- if((letter <= 0x241) && iswalpha(letter))
+ if((letter <= 0x241) && iswalpha2(letter))
{
sprintf(phonemes,"%c%s",phonSWITCH,tr->langopts.ascii_language);
return(0);
@@ -2564,14 +2753,19 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
#endif
// is it a bracket ?
+ if(letter == 0xe000+'(')
+ {
+ if(pre_pause < tr->langopts.param2[LOPT_BRACKET_PAUSE])
+ pre_pause = tr->langopts.param2[LOPT_BRACKET_PAUSE]; // a bracket, aleady spoken by AnnouncePunctuation()
+ }
if(IsBracket(letter))
{
- if(pre_pause < 4)
- pre_pause = 4;
+ if(pre_pause < tr->langopts.param[LOPT_BRACKET_PAUSE])
+ pre_pause = tr->langopts.param[LOPT_BRACKET_PAUSE];
}
// no match, try removing the accent and re-translating the word
- if((letter >= 0xc0) && (letter <= 0x241) && ((ix = remove_accent[letter-0xc0]) != 0))
+ if((letter >= 0xc0) && (letter < N_REMOVE_ACCENT) && ((ix = remove_accent[letter-0xc0]) != 0))
{
// within range of the remove_accent table
if((p[-2] != ' ') || (p[n] != ' '))
@@ -2581,14 +2775,14 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
p[-1] = ix;
while((p[0] = p[n]) != ' ') p++;
while(n-- > 0) *p++ = ' '; // replacement character must be no longer than original
-
+
if(tr->langopts.param[LOPT_DIERESES] && (lookupwchar(diereses_list,letter) > 0))
{
// vowel with dieresis, replace and continue from this point
p = p2;
continue;
}
-
+
phonemes[0] = 0; // delete any phonemes which have been produced so far
p = p_start;
tr->word_vowel_count = 0;
@@ -2596,15 +2790,20 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
continue; // start again at the beginning of the word
}
}
- else
- if((letter >= 0x3200) && (letter < 0xa700) && (end_phonemes != NULL))
+
+ if(((alphabet = AlphabetFromChar(letter)) != NULL) && (alphabet->offset != tr->letter_bits_offset))
{
- // ideograms
- // outside the range of the accent table, speak the unknown symbol sound
- Lookup(tr, "_??", ph_buf);
- match1.phonemes = ph_buf;
- match1.points = 1;
- p += (wc_bytes-1);
+ if(tr->langopts.alt_alphabet == alphabet->offset)
+ {
+ sprintf(phonemes,"%c%s",phonSWITCH, WordToString2(tr->langopts.alt_alphabet_lang));
+ return(0);
+ }
+ if(alphabet->flags & AL_WORDS)
+ {
+ // switch to the nominated language for this alphabet
+ sprintf(phonemes,"%c%s",phonSWITCH, WordToString2(alphabet->language));
+ return(0);
+ }
}
}
}
@@ -2615,8 +2814,7 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
{
// combining accent inside a word, ignore
}
- else
- if(IsAlpha(wc))
+ else if(IsAlpha(wc))
{
if((any_alpha > 1) || (p[wc_bytes-1] > ' '))
{
@@ -2629,7 +2827,7 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
}
else
{
- LookupLetter(tr, wc, -1, ph_buf);
+ LookupLetter(tr, wc, -1, ph_buf, 0);
if(ph_buf[0])
{
match1.phonemes = ph_buf;
@@ -2647,16 +2845,27 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
if(match1.phonemes == NULL)
match1.phonemes = "";
-
+
if(match1.points > 0)
{
+ if(word_flags & FLAG_UNPRON_TEST)
+ return(match1.end_type | 1);
+
+#ifdef deleted
+// ?? allow $unpr while translating rules, not just on initial FLAG_UNPRON_TEST
+ if((match1.end_type & SUFX_UNPRON) && !(word_flags & FLAG_SUFFIX_REMOVED))
+ return(match1.end_type);
+#endif
+
if((match1.phonemes[0] == phonSWITCH) && ((word_flags & FLAG_DONT_SWITCH_TRANSLATOR)==0))
{
// an instruction to switch language, return immediately so we can re-translate
strcpy(phonemes,match1.phonemes);
return(0);
}
-
+
+ match1.end_type &= ~SUFX_UNPRON;
+
if((match1.end_type != 0) && (end_phonemes != NULL))
{
/* a standard ending has been found, re-translate the word without it */
@@ -2682,8 +2891,6 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
}
}
- // any language specific changes ?
- ApplySpecialAttribute(tr,phonemes,dict_flags0);
memcpy(p_start,word_copy,strlen(word_copy));
return(0);
@@ -2692,16 +2899,15 @@ int TranslateRules(Translator *tr, char *p_start, char *phonemes, int ph_size, c
void ApplySpecialAttribute2(Translator *tr, char *phonemes, int dict_flags)
{//========================================================================
- // apply after the translation is complete
+// apply after the translation is complete
int ix;
int len;
char *p;
len = strlen(phonemes);
- switch(tr->translator_name)
+ if(tr->langopts.param[LOPT_ALT] & 2)
{
- case L('i','t'):
for(ix=0; ix<(len-1); ix++)
{
if(phonemes[ix] == phonSTRESS_P)
@@ -2724,62 +2930,10 @@ void ApplySpecialAttribute2(Translator *tr, char *phonemes, int dict_flags)
break;
}
}
- break;
}
} // end of ApplySpecialAttribute2
-void ApplySpecialAttribute(Translator *tr, char *phonemes, int dict_flags)
-{//=======================================================================
-// Amend the translated phonemes according to an attribute which is specific for the language.
- int len;
- int ix;
- char *p_end;
- int phoneme_1;
-
- if((dict_flags & (FLAG_ALT_TRANS | FLAG_ALT2_TRANS)) == 0)
- return;
-
- len = strlen(phonemes);
- p_end = &phonemes[len-1];
-
- switch(tr->translator_name)
- {
- case L('d','e'):
- if(p_end[0] == PhonemeCode2('i',':'))
- {
- // words ends in ['i:], change to [=I@]
- p_end[-1] = phonSTRESS_PREV;
- p_end[0] = PhonemeCode('I');
- p_end[1] = phonSCHWA;
- p_end[2] = 0;
- }
- break;
-
- case L('p','t'):
- phoneme_1 = PhonemeCode('o');
- for(ix=0; ix<(len-1); ix++)
- {
- if(phonemes[ix] == phoneme_1)
- {
- phonemes[ix] = PhonemeCode('O');
- break;
- }
- }
- break;
-
- case L('r','o'):
- if(p_end[0] == PhonemeCode('j'))
- {
- // word end in [j], change to ['i]
- p_end[0] = phonSTRESS_P;
- p_end[1] = PhonemeCode('i');
- p_end[2] = 0;
- }
- break;
- }
-} // end of ApplySpecialAttribute
-
//=============================================================================================
@@ -2788,70 +2942,69 @@ void ApplySpecialAttribute(Translator *tr, char *phonemes, int dict_flags)
// special properties, such as pronounce as unstressed
//=============================================================================================
-// common letter pairs, encode these as a single byte
-static const short pairs_ru[] = {
-0x010c, // ла 21052 0x23
-0x010e, // на 18400
-0x0113, // та 14254
-0x0301, // ав 31083
-0x030f, // ов 13420
-0x060e, // не 21798
-0x0611, // ре 19458
-0x0903, // ви 16226
-0x0b01, // ак 14456
-0x0b0f, // ок 17836
-0x0c01, // ал 13324
-0x0c09, // ил 16877
-0x0e01, // ан 15359
-0x0e06, // ен 13543 0x30
-0x0e09, // ин 17168
-0x0e0e, // нн 15973
-0x0e0f, // он 22373
-0x0e1c, // ын 15052
-0x0f03, // во 24947
-0x0f11, // ро 13552
-0x0f12, // со 16368
-0x100f, // оп 19054
-0x1011, // рп 17067
-0x1101, // ар 23967
-0x1106, // ер 18795
-0x1109, // ир 13797
-0x110f, // ор 21737
-0x1213, // тс 25076
-0x1220, // яс 14310
-0x7fff};
-//0x040f ог 12976
-//0x1306 ет 12826
-//0x0f0d мо 12688
-
-
-int TransposeAlphabet(char *text, int offset, int min, int max)
-{//============================================================
+
+int TransposeAlphabet(Translator *tr, char *text)
+{//==============================================
// transpose cyrillic alphabet (for example) into ascii (single byte) character codes
// return: number of bytes, bit 6: 1=used compression
int c;
int c2;
int ix;
+ int offset;
+ int min;
+ int max;
+ const char *map;
char *p = text;
- char *p2 = text;
+ char *p2;
int all_alpha=1;
int bits;
int acc;
+ int pairs_start;
+ const short *pairs_list;
+ int bufix;
+ char buf[N_WORD_BYTES+1];
+
+
+ offset = tr->transpose_min - 1;
+ min = tr->transpose_min;
+ max = tr->transpose_max;
+ map = tr->transpose_map;
+ pairs_start = max - min + 2;
+
+ bufix = 0;
do {
p += utf8_in(&c,p);
- if((c >= min) && (c <= max))
- {
- *p2++ = c - offset;
- }
- else
if(c != 0)
{
- p2 += utf8_out(c,p2);
- all_alpha=0;
+ if((c >= min) && (c <= max))
+ {
+ if(map == NULL)
+ {
+ buf[bufix++] = c - offset;
+ }
+ else
+ {
+ // get the code from the transpose map
+ if(map[c - min] > 0)
+ {
+ buf[bufix++] = map[c - min];
+ }
+ else
+ {
+ all_alpha=0;
+ break;
+ }
+ }
+ }
+ else
+ {
+ all_alpha=0;
+ break;
+ }
}
- } while (c != 0);
- *p2 = 0;
+ } while ((c != 0) && (bufix < N_WORD_BYTES));
+ buf[bufix] = 0;
if(all_alpha)
{
@@ -2859,19 +3012,22 @@ int TransposeAlphabet(char *text, int offset, int min, int max)
acc=0;
bits=0;
- p = text;
- p2 = text;
+ p = buf;
+ p2 = buf;
while((c = *p++) != 0)
{
- c2 = c + (*p << 8);
- for(ix=0; c2 >= pairs_ru[ix]; ix++)
+ if((pairs_list = tr->frequent_pairs) != NULL)
{
- if(c2 == pairs_ru[ix])
+ c2 = c + (*p << 8);
+ for(ix=0; c2 >= pairs_list[ix]; ix++)
{
- // found an encoding for a 2-character pair
- c = ix + 0x23; // 2-character codes start at 0x23
- p++;
- break;
+ if(c2 == pairs_list[ix])
+ {
+ // found an encoding for a 2-character pair
+ c = ix + pairs_start; // 2-character codes start after the single letter codes
+ p++;
+ break;
+ }
}
}
acc = (acc << 6) + (c & 0x3f);
@@ -2880,7 +3036,7 @@ int TransposeAlphabet(char *text, int offset, int min, int max)
if(bits >= 8)
{
bits -= 8;
- *p2++ = (acc >> bits);
+ *p2++ = (acc >> bits);
}
}
if(bits > 0)
@@ -2888,16 +3044,21 @@ int TransposeAlphabet(char *text, int offset, int min, int max)
*p2++ = (acc << (8-bits));
}
*p2 = 0;
- return((p2 - text) | 0x40); // bit 6 indicates compressed characters
+ ix = p2 - buf;
+ memcpy(text, buf, ix);
+ return(ix | 0x40); // bit 6 indicates compressed characters
+ }
+ else
+ {
+ return(strlen(text));
}
- return(p2 - text);
} // end of TransposeAlphabet
static const char *LookupDict2(Translator *tr, const char *word, const char *word2,
- char *phonetic, unsigned int *flags, int end_flags, WORD_TAB *wtab)
+ char *phonetic, unsigned int *flags, int end_flags, WORD_TAB *wtab)
//=====================================================================================
/* Find an entry in the word_dict file for a specified word.
Returns NULL if no match, else returns 'word_end'
@@ -2923,21 +3084,25 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
int no_phonemes;
int skipwords;
int ix;
+ int c;
const char *word_end;
const char *word1;
int wflags = 0;
- char word_buf[N_WORD_BYTES];
+ int lookup_symbol;
+ char word_buf[N_WORD_BYTES+1];
+ char dict_flags_buf[80];
if(wtab != NULL)
{
wflags = wtab->flags;
}
+ lookup_symbol = flags[1] & FLAG_LOOKUP_SYMBOL;
word1 = word;
- if(tr->transpose_offset > 0)
+ if(tr->transpose_min > 0)
{
- strcpy(word_buf,word);
- wlen = TransposeAlphabet(word_buf, tr->transpose_offset, tr->transpose_min, tr->transpose_max);
+ strncpy0(word_buf,word, N_WORD_BYTES);
+ wlen = TransposeAlphabet(tr, word_buf); // bit 6 indicates compressed characters
word = word_buf;
}
else
@@ -3011,8 +3176,7 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
condition_failed = 1;
}
}
- else
- if(flag > 80)
+ else if(flag > 80)
{
// flags 81 to 90 match more than one word
// This comes after the other flags
@@ -3020,11 +3184,16 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
skipwords = flag - 80;
// don't use the contraction if any of the words are emphasized
- for(ix=0; ix <= skipwords; ix++)
+ // or has an embedded command, such as MARK
+ if(wtab != NULL)
{
- if(wflags & FLAG_EMPHASIZED2)
+ for(ix=0; ix <= skipwords; ix++)
{
- condition_failed = 1;
+ if(wtab[ix].flags & FLAG_EMPHASIZED2)
+// if(((wflags2 = wtab[ix].flags) & FLAG_EMPHASIZED2) || ((ix > 0) && (wflags2 & FLAG_EMBEDDED)))
+ {
+ condition_failed = 1;
+ }
}
}
@@ -3042,16 +3211,14 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
p = next;
word_end = word2 + n_chars;
}
- else
- if(flag > 64)
+ else if(flag > 64)
{
// stressed syllable information, put in bits 0-3
dictionary_flags = (dictionary_flags & ~0xf) | (flag & 0xf);
if((flag & 0xc) == 0xc)
dictionary_flags |= FLAG_STRESS_END;
}
- else
- if(flag >= 32)
+ else if(flag >= 32)
{
dictionary_flags2 |= (1L << (flag-32));
}
@@ -3070,20 +3237,20 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
if((end_flags & FLAG_SUFX)==0)
{
// no suffix has been removed
- if(dictionary_flags & FLAG_STEM)
+ if(dictionary_flags2 & FLAG_STEM)
continue; // this word must have a suffix
}
- if((end_flags & SUFX_P) && (dictionary_flags & (FLAG_ONLY | FLAG_ONLY_S)))
+ if((end_flags & SUFX_P) && (dictionary_flags2 & (FLAG_ONLY | FLAG_ONLY_S)))
continue; // $only or $onlys, don't match if a prefix has been removed
if(end_flags & FLAG_SUFX)
{
// a suffix was removed from the word
- if(dictionary_flags & FLAG_ONLY)
+ if(dictionary_flags2 & FLAG_ONLY)
continue; // no match if any suffix
- if((dictionary_flags & FLAG_ONLY_S) && ((end_flags & FLAG_SUFX_S)==0))
+ if((dictionary_flags2 & FLAG_ONLY_S) && ((end_flags & FLAG_SUFX_S)==0))
{
// only a 's' suffix allowed, but the suffix wasn't 's'
continue;
@@ -3111,10 +3278,27 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
continue;
}
}
+ if(dictionary_flags & FLAG_NEEDS_DOT)
+ {
+ if(!(wflags & FLAG_HAS_DOT))
+ continue;
+ }
+
+ if((dictionary_flags2 & FLAG_ATEND) && (word_end < translator->clause_end) && (lookup_symbol==0))
+ {
+ // only use this pronunciation if it's the last word of the clause, or called from Lookup()
+ continue;
+ }
+
+ if((dictionary_flags2 & FLAG_ATSTART) && !(wtab->flags & FLAG_FIRST_WORD))
+ {
+ // only use this pronunciation if it's the first word of a clause
+ continue;
+ }
- if((dictionary_flags & FLAG_ATEND) && (word_end < tr->clause_end))
+ if((dictionary_flags2 & FLAG_SENTENCE) && !(translator->clause_terminator & CLAUSE_BIT_SENTENCE))
{
- // only use this pronunciation if it's the last word of the clause
+ // only if this clause is a sentence , i.e. terminator is {. ? !} not {, : :}
continue;
}
@@ -3125,6 +3309,11 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
if(tr->expect_verb || (tr->expect_verb_s && (end_flags & FLAG_SUFX_S)))
{
// OK, we are expecting a verb
+ if((tr->translator_name == L('e','n')) && (tr->prev_dict_flags[0] & FLAG_ALT6_TRANS) && (end_flags & FLAG_SUFX_S))
+ {
+ // lang=en, don't use verb form after 'to' if the word has 's' suffix
+ continue;
+ }
}
else
{
@@ -3144,13 +3333,24 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
}
if(dictionary_flags2 & FLAG_NOUN)
{
- if(!tr->expect_noun)
+ if((!tr->expect_noun) || (end_flags & SUFX_V))
{
/* don't use the 'noun' pronunciation unless we are
expecting a noun */
continue;
}
}
+ if(dictionary_flags2 & FLAG_NATIVE)
+ {
+ if(tr != translator)
+ continue; // don't use if we've switched translators
+ }
+ if(dictionary_flags & FLAG_ALT2_TRANS)
+ {
+ // language specific
+ if((tr->translator_name == L('h','u')) && !(tr->prev_dict_flags[0] & FLAG_ALT_TRANS))
+ continue;
+ }
if(flags != NULL)
{
@@ -3162,23 +3362,21 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
{
if(option_phonemes == 2)
{
- fprintf(f_trans,"Flags: %s %s\n",word1,print_dictionary_flags(flags));
+ print_dictionary_flags(flags, dict_flags_buf, sizeof(dict_flags_buf));
+ fprintf(f_trans,"Flags: %s %s\n", word1, dict_flags_buf);
}
return(0); // no phoneme translation found here, only flags. So use rules
}
if(flags != NULL)
flags[0] |= FLAG_FOUND; // this flag indicates word was found in dictionary
-
+
if(option_phonemes == 2)
{
- unsigned int flags1 = 0;
char ph_decoded[N_WORD_PHONEMES];
int textmode;
DecodePhonemes(phonetic,ph_decoded);
- if(flags != NULL)
- flags1 = flags[0];
if((dictionary_flags & FLAG_TEXTMODE) == 0)
textmode = 0;
@@ -3188,9 +3386,28 @@ static const char *LookupDict2(Translator *tr, const char *word, const char *wor
if(textmode == translator->langopts.textmode)
{
// only show this line if the word translates to phonemes, not replacement text
- fprintf(f_trans,"Found: %s [%s] %s\n",word1,ph_decoded,print_dictionary_flags(flags));
+ if((dictionary_flags & FLAG_SKIPWORDS) && (wtab != NULL))
+ {
+ // matched more than one word
+ // (check for wtab prevents showing RULE_SPELLING byte when speaking individual letters)
+ memcpy(word_buf,word2,word_end-word2);
+ word_buf[word_end-word2-1] = 0;
+ fprintf(f_trans,"Found: '%s %s\n",word1,word_buf);
+ }
+ else
+ {
+ fprintf(f_trans,"Found: '%s",word1);
+ }
+ print_dictionary_flags(flags, dict_flags_buf, sizeof(dict_flags_buf));
+ fprintf(f_trans,"' [%s] %s\n", ph_decoded,dict_flags_buf);
}
}
+
+ ix = utf8_in(&c, word);
+ if((word[ix] == 0) && !IsAlpha(c))
+ {
+ flags[0] |= FLAG_MAX3;
+ }
return(word_end);
}
@@ -3249,16 +3466,21 @@ int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *f
}
}
- for(length=0; length<N_WORD_BYTES; length++)
+ for(length=0; length<(N_WORD_BYTES-1); length++)
{
if(((c = *word1++)==0) || (c == ' '))
break;
+
+ if((c=='.') && (length > 0) && (IsDigit09(word[length-1])))
+ break; // needed for lang=hu, eg. "december 2.-ig"
+
word[length] = c;
}
word[length] = 0;
found = LookupDict2(tr, word, word1, ph_out, flags, end_flags, wtab);
+
if(flags[0] & FLAG_MAX3)
{
if(strcmp(ph_out, tr->phonemes_repeat) == 0)
@@ -3294,17 +3516,16 @@ int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *f
if(found == 0)
{
ph_out[0] = 0;
-
+
// try modifications to find a recognised word
-
+
if((end_flags & FLAG_SUFX_E_ADDED) && (word[length-1] == 'e'))
{
// try removing an 'e' which has been added by RemoveEnding
word[length-1] = 0;
found = LookupDict2(tr, word, word1, ph_out, flags, end_flags, wtab);
}
- else
- if((end_flags & SUFX_D) && (word[length-1] == word[length-2]))
+ else if((end_flags & SUFX_D) && (word[length-1] == word[length-2]))
{
// try removing a double letter
word[length-1] = 0;
@@ -3340,6 +3561,10 @@ int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *f
fprintf(f_trans,"Replace: %s %s\n",word,*wordptr);
}
}
+ else
+ {
+// flags[0] &= ~FLAG_SKIPWORDS; // check lang=hu január 21.-ig (error: suffix repeated ??)
+ }
ph_out[0] = 0;
return(0);
@@ -3353,12 +3578,44 @@ int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *f
} // end of LookupDictList
+extern char word_phonemes[N_WORD_PHONEMES]; // a word translated into phoneme codes
int Lookup(Translator *tr, const char *word, char *ph_out)
{//===================================================
- unsigned int flags[2]={0,0};
- char* word1 = (char *)word;
- return(LookupDictList(tr, &word1, ph_out, flags, 0, NULL));
+ int found;
+ unsigned int flags[2];
+ int say_as;
+ char *word1 = (char *)word;
+ char text[80];
+
+ flags[0] = 0;
+ flags[1] = FLAG_LOOKUP_SYMBOL;
+ found = LookupDictList(tr, &word1, ph_out, flags, FLAG_ALLOW_TEXTMODE, NULL);
+
+ if(flags[0] & FLAG_TEXTMODE)
+ {
+ say_as = option_sayas;
+ option_sayas = 0; // don't speak replacement word as letter names
+ text[0] = 0;
+ strncpy0(&(text[1]), word1, sizeof(text)-2);
+ found = TranslateWord(tr, &text[1], 0, NULL, NULL);
+ strcpy(ph_out, word_phonemes);
+ option_sayas = say_as;
+ }
+ return(found);
+}
+
+
+int LookupFlags(Translator *tr, const char *word, unsigned int **flags_out)
+{//===========================================================================
+ char buf[100];
+ static unsigned int flags[2];
+ char *word1 = (char *)word;
+
+ flags[0] = flags[1] = 0;
+ LookupDictList(tr, &word1, buf, flags, 0, NULL);
+ *flags_out = flags;
+ return(flags[0]);
}
@@ -3367,27 +3624,30 @@ int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy)
{//========================================================================
/* Removes a standard suffix from a word, once it has been indicated by the dictionary rules.
end_type: bits 0-6 number of letters
- bits 8-14 suffix flags
+ bits 8-14 suffix flags
word_copy: make a copy of the original word
This routine is language specific. In English it deals with reversing y->i and e-dropping
that were done when the suffix was added to the original word.
*/
-
+
int i;
char *word_end;
int len_ending;
int end_flags;
const char *p;
int len;
- static char ending[12];
-
+ char ending[50];
+
// these lists are language specific, but are only relevent if the 'e' suffix flag is used
static const char *add_e_exceptions[] = {
- "ion", NULL };
+ "ion", NULL
+ };
static const char *add_e_additions[] = {
- "c", "rs", "ir", "ur", "ath", "ns", "lu", NULL };
+// "c", "rs", "ir", "ur", "ath", "ns", "lu", NULL };
+ "c", "rs", "ir", "ur", "ath", "ns", "u", NULL
+ };
for(word_end = word; *word_end != ' '; word_end++)
{
@@ -3396,11 +3656,15 @@ int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy)
*word_end = 'e';
}
i = word_end - word;
- memcpy(word_copy,word,i);
- word_copy[i] = 0;
+
+ if(word_copy != NULL)
+ {
+ memcpy(word_copy,word,i);
+ word_copy[i] = 0;
+ }
// look for multibyte characters to increase the number of bytes to remove
- for(len_ending = i = (end_type & 0x3f); i>0 ;i--) // num.of characters of the suffix
+ for(len_ending = i = (end_type & 0x3f); i>0 ; i--) // num.of characters of the suffix
{
word_end--;
while((*word_end & 0xc0) == 0x80)
@@ -3409,9 +3673,9 @@ int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy)
len_ending++;
}
}
-
+
// remove bytes from the end of the word and replace them by spaces
- for(i=0; i<len_ending; i++)
+ for(i=0; (i<len_ending) && (i < (int)sizeof(ending)-1); i++)
{
ending[i] = word_end[i];
word_end[i] = ' ';
@@ -3420,57 +3684,73 @@ int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy)
word_end--; /* now pointing at last character of stem */
end_flags = (end_type & 0xfff0) | FLAG_SUFX;
-
+
/* add an 'e' to the stem if appropriate,
if stem ends in vowel+consonant
or stem ends in 'c' (add 'e' to soften it) */
-
+
if(end_type & SUFX_I)
{
if(word_end[0] == 'i')
word_end[0] = 'y';
}
-
+
if(end_type & SUFX_E)
{
- // add 'e' to end of stem
- if(IsLetter(tr, word_end[-1],LETTERGP_VOWEL2) && IsLetter(tr, word_end[0],1))
+ if(tr->translator_name == L('n','l'))
{
- // vowel(incl.'y') + hard.consonant
-
- for(i=0; (p = add_e_exceptions[i]) != NULL; i++)
+ if(((word_end[0] & 0x80) == 0) && ((word_end[-1] & 0x80) == 0) && IsVowel(tr, word_end[-1]) && IsLetter(tr, word_end[0], LETTERGP_C) && !IsVowel(tr, word_end[-2]))
{
- len = strlen(p);
- if(memcmp(p,&word_end[1-len],len)==0)
- {
- break;
- }
+ //double the vowel before the (ascii) final consonant
+ word_end[1] = word_end[0];
+ word_end[0] = word_end[-1];
+ word_end[2] = ' ';
}
- if(p == NULL)
- end_flags |= FLAG_SUFX_E_ADDED; // no exception found
}
- else
+ else if(tr->translator_name == L('e','n'))
{
- for(i=0; (p = add_e_additions[i]) != NULL; i++)
+ // add 'e' to end of stem
+ if(IsLetter(tr, word_end[-1],LETTERGP_VOWEL2) && IsLetter(tr, word_end[0],1))
{
- len = strlen(p);
- if(memcmp(p,&word_end[1-len],len)==0)
+ // vowel(incl.'y') + hard.consonant
+
+ for(i=0; (p = add_e_exceptions[i]) != NULL; i++)
{
- end_flags |= FLAG_SUFX_E_ADDED;
- break;
+ len = strlen(p);
+ if(memcmp(p,&word_end[1-len],len)==0)
+ {
+ break;
+ }
+ }
+ if(p == NULL)
+ end_flags |= FLAG_SUFX_E_ADDED; // no exception found
+ }
+ else
+ {
+ for(i=0; (p = add_e_additions[i]) != NULL; i++)
+ {
+ len = strlen(p);
+ if(memcmp(p,&word_end[1-len],len)==0)
+ {
+ end_flags |= FLAG_SUFX_E_ADDED;
+ break;
+ }
}
}
}
+ else if(tr->langopts.suffix_add_e != 0)
+ {
+ end_flags |= FLAG_SUFX_E_ADDED;
+ }
if(end_flags & FLAG_SUFX_E_ADDED)
{
- word_end[1] = 'e';
-#ifdef LOG_TRANSLATE
-if(option_phonemes == 2)
-{
- fprintf(f_trans,"add e\n");
-}
-#endif
+ utf8_out(tr->langopts.suffix_add_e, &word_end[1]);
+
+ if(option_phonemes == 2)
+ {
+ fprintf(f_trans,"add e\n");
+ }
}
}
@@ -3481,10 +3761,12 @@ if(option_phonemes == 2)
if((strcmp(ending,"s")==0) || (strcmp(ending,"es")==0))
end_flags |= FLAG_SUFX_S;
- if(strcmp(ending,"'s")==0)
+// if(strcmp(ending,"'s")==0)
+ if(ending[0] == '\'')
end_flags &= ~FLAG_SUFX; // don't consider 's as an added suffix
return(end_flags);
} /* end of RemoveEnding */
+
diff --git a/navit/support/espeak/dictionary.h b/navit/support/espeak/dictionary.h
new file mode 100644
index 000000000..53a5d7c7a
--- /dev/null
+++ b/navit/support/espeak/dictionary.h
@@ -0,0 +1,19 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2010 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+int HashDictionary(const char *string);
diff --git a/navit/support/espeak/espeak-data/af_dict b/navit/support/espeak/espeak-data/af_dict
index 7819c9fe0..d9afc14a0 100644
--- a/navit/support/espeak/espeak-data/af_dict
+++ b/navit/support/espeak/espeak-data/af_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/am_dict b/navit/support/espeak/espeak-data/am_dict
new file mode 100644
index 000000000..0e8a62ce9
--- /dev/null
+++ b/navit/support/espeak/espeak-data/am_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/an_dict b/navit/support/espeak/espeak-data/an_dict
new file mode 100644
index 000000000..f716d6226
--- /dev/null
+++ b/navit/support/espeak/espeak-data/an_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/as_dict b/navit/support/espeak/espeak-data/as_dict
new file mode 100644
index 000000000..1a04accda
--- /dev/null
+++ b/navit/support/espeak/espeak-data/as_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/az_dict b/navit/support/espeak/espeak-data/az_dict
new file mode 100644
index 000000000..e49c57a67
--- /dev/null
+++ b/navit/support/espeak/espeak-data/az_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/bg_dict b/navit/support/espeak/espeak-data/bg_dict
new file mode 100644
index 000000000..79a49c66c
--- /dev/null
+++ b/navit/support/espeak/espeak-data/bg_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/bn_dict b/navit/support/espeak/espeak-data/bn_dict
new file mode 100644
index 000000000..b6fd7f820
--- /dev/null
+++ b/navit/support/espeak/espeak-data/bn_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ca_dict b/navit/support/espeak/espeak-data/ca_dict
index b9a4ea43a..bfb99080c 100644
--- a/navit/support/espeak/espeak-data/ca_dict
+++ b/navit/support/espeak/espeak-data/ca_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/config b/navit/support/espeak/espeak-data/config
deleted file mode 100755
index be1b6246a..000000000
--- a/navit/support/espeak/espeak-data/config
+++ /dev/null
@@ -1,9 +0,0 @@
-//pa_device 7
-
-// play a sound for punctuation, rather than speak its name
-//soundicon _( /usr/share/sounds/sound-icons/left-round-bracket
-//soundicon _) /usr/share/sounds/sound-icons/right-round-bracket
-//soundicon _[ /usr/share/sounds/sound-icons/left-square-bracket
-//soundicon _] /usr/share/sounds/sound-icons/right-square-bracket
-//soundicon _{ /usr/share/sounds/sound-icons/left-brace
-//soundicon _} /usr/share/sounds/sound-icons/right-brace
diff --git a/navit/support/espeak/espeak-data/cs_dict b/navit/support/espeak/espeak-data/cs_dict
index 3391b98b2..7a1dca12b 100644
--- a/navit/support/espeak/espeak-data/cs_dict
+++ b/navit/support/espeak/espeak-data/cs_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/cy_dict b/navit/support/espeak/espeak-data/cy_dict
index 13a768030..ee4be2ecd 100644
--- a/navit/support/espeak/espeak-data/cy_dict
+++ b/navit/support/espeak/espeak-data/cy_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/da_dict b/navit/support/espeak/espeak-data/da_dict
index 75ccbd670..e362944bb 100644
--- a/navit/support/espeak/espeak-data/da_dict
+++ b/navit/support/espeak/espeak-data/da_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/de_dict b/navit/support/espeak/espeak-data/de_dict
index 988f4c31b..bc800be2a 100644
--- a/navit/support/espeak/espeak-data/de_dict
+++ b/navit/support/espeak/espeak-data/de_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/el_dict b/navit/support/espeak/espeak-data/el_dict
index a6ab63dee..7059f393e 100644
--- a/navit/support/espeak/espeak-data/el_dict
+++ b/navit/support/espeak/espeak-data/el_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/en_dict b/navit/support/espeak/espeak-data/en_dict
index d15b0e9ab..b363a4cc7 100644
--- a/navit/support/espeak/espeak-data/en_dict
+++ b/navit/support/espeak/espeak-data/en_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/eo_dict b/navit/support/espeak/espeak-data/eo_dict
index 155523963..1f75c9c86 100644
--- a/navit/support/espeak/espeak-data/eo_dict
+++ b/navit/support/espeak/espeak-data/eo_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/es_dict b/navit/support/espeak/espeak-data/es_dict
index 203da2639..8e2669aa9 100644
--- a/navit/support/espeak/espeak-data/es_dict
+++ b/navit/support/espeak/espeak-data/es_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/et_dict b/navit/support/espeak/espeak-data/et_dict
new file mode 100644
index 000000000..36501b23e
--- /dev/null
+++ b/navit/support/espeak/espeak-data/et_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/eu_dict b/navit/support/espeak/espeak-data/eu_dict
new file mode 100644
index 000000000..7b4a788df
--- /dev/null
+++ b/navit/support/espeak/espeak-data/eu_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/fa_dict b/navit/support/espeak/espeak-data/fa_dict
new file mode 100644
index 000000000..82d8a6934
--- /dev/null
+++ b/navit/support/espeak/espeak-data/fa_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/fi_dict b/navit/support/espeak/espeak-data/fi_dict
index a08d45a6f..17bf66ed8 100644
--- a/navit/support/espeak/espeak-data/fi_dict
+++ b/navit/support/espeak/espeak-data/fi_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/fr_dict b/navit/support/espeak/espeak-data/fr_dict
index e66708e04..c4551e5cb 100644
--- a/navit/support/espeak/espeak-data/fr_dict
+++ b/navit/support/espeak/espeak-data/fr_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ga_dict b/navit/support/espeak/espeak-data/ga_dict
new file mode 100644
index 000000000..f8b912cb0
--- /dev/null
+++ b/navit/support/espeak/espeak-data/ga_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/gd_dict b/navit/support/espeak/espeak-data/gd_dict
new file mode 100644
index 000000000..a048417aa
--- /dev/null
+++ b/navit/support/espeak/espeak-data/gd_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/grc_dict b/navit/support/espeak/espeak-data/grc_dict
index 0aeab45e0..b69e75513 100644
--- a/navit/support/espeak/espeak-data/grc_dict
+++ b/navit/support/espeak/espeak-data/grc_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/gu_dict b/navit/support/espeak/espeak-data/gu_dict
new file mode 100644
index 000000000..25a6c5567
--- /dev/null
+++ b/navit/support/espeak/espeak-data/gu_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/hbs_dict b/navit/support/espeak/espeak-data/hbs_dict
index 91ee7ba4b..827b44df9 100644
--- a/navit/support/espeak/espeak-data/hbs_dict
+++ b/navit/support/espeak/espeak-data/hbs_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/hi_dict b/navit/support/espeak/espeak-data/hi_dict
index 388d3f246..bad7d779c 100644
--- a/navit/support/espeak/espeak-data/hi_dict
+++ b/navit/support/espeak/espeak-data/hi_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/hu_dict b/navit/support/espeak/espeak-data/hu_dict
index 5a1f21165..6c16dbe12 100644
--- a/navit/support/espeak/espeak-data/hu_dict
+++ b/navit/support/espeak/espeak-data/hu_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/hy_dict b/navit/support/espeak/espeak-data/hy_dict
index 18c3e2027..39c321e7a 100644
--- a/navit/support/espeak/espeak-data/hy_dict
+++ b/navit/support/espeak/espeak-data/hy_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/id_dict b/navit/support/espeak/espeak-data/id_dict
index 3c9967ce8..5bd43b735 100644
--- a/navit/support/espeak/espeak-data/id_dict
+++ b/navit/support/espeak/espeak-data/id_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/intonations b/navit/support/espeak/espeak-data/intonations
new file mode 100644
index 000000000..f1e31ce9e
--- /dev/null
+++ b/navit/support/espeak/espeak-data/intonations
Binary files differ
diff --git a/navit/support/espeak/espeak-data/is_dict b/navit/support/espeak/espeak-data/is_dict
index 10cf846f8..477ef7eca 100644
--- a/navit/support/espeak/espeak-data/is_dict
+++ b/navit/support/espeak/espeak-data/is_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/it_dict b/navit/support/espeak/espeak-data/it_dict
index 40cf952a2..5b431044c 100644
--- a/navit/support/espeak/espeak-data/it_dict
+++ b/navit/support/espeak/espeak-data/it_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/jbo_dict b/navit/support/espeak/espeak-data/jbo_dict
index f7ebcf454..ac2a95282 100644
--- a/navit/support/espeak/espeak-data/jbo_dict
+++ b/navit/support/espeak/espeak-data/jbo_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ka_dict b/navit/support/espeak/espeak-data/ka_dict
new file mode 100644
index 000000000..0cf221e0c
--- /dev/null
+++ b/navit/support/espeak/espeak-data/ka_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/kl_dict b/navit/support/espeak/espeak-data/kl_dict
new file mode 100644
index 000000000..e3031a055
--- /dev/null
+++ b/navit/support/espeak/espeak-data/kl_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/kn_dict b/navit/support/espeak/espeak-data/kn_dict
new file mode 100644
index 000000000..f750d90c3
--- /dev/null
+++ b/navit/support/espeak/espeak-data/kn_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ko_dict b/navit/support/espeak/espeak-data/ko_dict
new file mode 100644
index 000000000..d7c3c8a1d
--- /dev/null
+++ b/navit/support/espeak/espeak-data/ko_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ku_dict b/navit/support/espeak/espeak-data/ku_dict
index 18d285845..17ee6f1d3 100644
--- a/navit/support/espeak/espeak-data/ku_dict
+++ b/navit/support/espeak/espeak-data/ku_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/la_dict b/navit/support/espeak/espeak-data/la_dict
index 3ef82f832..9faecc466 100644
--- a/navit/support/espeak/espeak-data/la_dict
+++ b/navit/support/espeak/espeak-data/la_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/lfn_dict b/navit/support/espeak/espeak-data/lfn_dict
new file mode 100644
index 000000000..6f2445e2f
--- /dev/null
+++ b/navit/support/espeak/espeak-data/lfn_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/lt_dict b/navit/support/espeak/espeak-data/lt_dict
new file mode 100644
index 000000000..1e2daa7cf
--- /dev/null
+++ b/navit/support/espeak/espeak-data/lt_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/lv_dict b/navit/support/espeak/espeak-data/lv_dict
index 3ecb61b51..158cf7baf 100644
--- a/navit/support/espeak/espeak-data/lv_dict
+++ b/navit/support/espeak/espeak-data/lv_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/af1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/af1_phtrans
index fc9ad0179..a87ec4ccd 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/af1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/af1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/cr1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/cr1_phtrans
index 15e2c4988..064669eaf 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/cr1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/cr1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/de2_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/de2_phtrans
index c5af1a7c4..804a0d401 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/de2_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/de2_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/de4_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/de4_phtrans
index b10fc8441..e626a582b 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/de4_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/de4_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/de6_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/de6_phtrans
index 4cb62d9c2..2aa70f4c4 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/de6_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/de6_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/ee1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/ee1_phtrans
new file mode 100644
index 000000000..32c569a2d
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/ee1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/en1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/en1_phtrans
index c847d1703..c32db3968 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/en1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/en1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/es_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/es_phtrans
index b959f92c1..958799c61 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/es_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/es_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/fr1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/fr1_phtrans
index 1a242b36f..dc0fcef7e 100644..100755
--- a/navit/support/espeak/espeak-data/mbrola_ph/fr1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/fr1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/gr2_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/gr2_phtrans
index b3775abbd..e7dca3af2 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/gr2_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/gr2_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/hn1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/hn1_phtrans
new file mode 100644
index 000000000..b2560ce77
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/hn1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/hu1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/hu1_phtrans
index 26dad49a0..eb661d3ab 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/hu1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/hu1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/ic1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/ic1_phtrans
new file mode 100644
index 000000000..978ec49ed
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/ic1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/id1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/id1_phtrans
index 452de8c8a..bd4b90cb0 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/id1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/id1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/in1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/in1_phtrans
index 7f4631899..cf1c58956 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/in1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/in1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/ir1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/ir1_phtrans
new file mode 100644
index 000000000..671873e7a
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/ir1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/it3_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/it3_phtrans
index 6d826477b..c9174cf0a 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/it3_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/it3_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/lt1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/lt1_phtrans
new file mode 100644
index 000000000..1066dfbc1
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/lt1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/lt2_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/lt2_phtrans
new file mode 100644
index 000000000..1066dfbc1
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/lt2_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/mx1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/mx1_phtrans
new file mode 100644
index 000000000..3b3e6f37f
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/mx1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/mx2_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/mx2_phtrans
new file mode 100644
index 000000000..a9bbb0f60
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/mx2_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/nl_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/nl_phtrans
index d982c1845..1edb05ae5 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/nl_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/nl_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/pl1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/pl1_phtrans
index 9d4e50fd7..5c3583da4 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/pl1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/pl1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/pt1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/pt1_phtrans
index c5172f7b3..f101c661f 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/pt1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/pt1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/ptbr4_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/ptbr4_phtrans
index 0b94de719..25bfdbf47 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/ptbr4_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/ptbr4_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/ptbr_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/ptbr_phtrans
index a1dbba000..f11ad8703 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/ptbr_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/ptbr_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/ro1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/ro1_phtrans
index 4aeaf54ec..4dec15997 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/ro1_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/ro1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/sv2_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/sv2_phtrans
index ae119d86e..805608fbc 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/sv2_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/sv2_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/sv_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/sv_phtrans
index bb556eb28..fcd75c4d3 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/sv_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/sv_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/tr1_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/tr1_phtrans
new file mode 100644
index 000000000..0d6fa2a91
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/tr1_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/us3_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/us3_phtrans
index 449b419ac..90607e183 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/us3_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/us3_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/us_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/us_phtrans
index bdeef5d1a..f0af93348 100644
--- a/navit/support/espeak/espeak-data/mbrola_ph/us_phtrans
+++ b/navit/support/espeak/espeak-data/mbrola_ph/us_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mbrola_ph/vz_phtrans b/navit/support/espeak/espeak-data/mbrola_ph/vz_phtrans
new file mode 100644
index 000000000..0df524f08
--- /dev/null
+++ b/navit/support/espeak/espeak-data/mbrola_ph/vz_phtrans
Binary files differ
diff --git a/navit/support/espeak/espeak-data/mk_dict b/navit/support/espeak/espeak-data/mk_dict
index a6cf3dc53..a682135e8 100644
--- a/navit/support/espeak/espeak-data/mk_dict
+++ b/navit/support/espeak/espeak-data/mk_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ml_dict b/navit/support/espeak/espeak-data/ml_dict
new file mode 100644
index 000000000..4d7f6523f
--- /dev/null
+++ b/navit/support/espeak/espeak-data/ml_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ms_dict b/navit/support/espeak/espeak-data/ms_dict
new file mode 100644
index 000000000..8a3b0364b
--- /dev/null
+++ b/navit/support/espeak/espeak-data/ms_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/nci_dict b/navit/support/espeak/espeak-data/nci_dict
new file mode 100644
index 000000000..34ff50deb
--- /dev/null
+++ b/navit/support/espeak/espeak-data/nci_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ne_dict b/navit/support/espeak/espeak-data/ne_dict
new file mode 100644
index 000000000..b55060251
--- /dev/null
+++ b/navit/support/espeak/espeak-data/ne_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/nl_dict b/navit/support/espeak/espeak-data/nl_dict
index f286bee90..7a8996443 100644
--- a/navit/support/espeak/espeak-data/nl_dict
+++ b/navit/support/espeak/espeak-data/nl_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/no_dict b/navit/support/espeak/espeak-data/no_dict
index 4f8f84f0d..aaf03015e 100644
--- a/navit/support/espeak/espeak-data/no_dict
+++ b/navit/support/espeak/espeak-data/no_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/or_dict b/navit/support/espeak/espeak-data/or_dict
new file mode 100644
index 000000000..1d023bb86
--- /dev/null
+++ b/navit/support/espeak/espeak-data/or_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/pa_dict b/navit/support/espeak/espeak-data/pa_dict
new file mode 100644
index 000000000..dc1bcdb34
--- /dev/null
+++ b/navit/support/espeak/espeak-data/pa_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/pap_dict b/navit/support/espeak/espeak-data/pap_dict
index 11eb4b515..b39bfe873 100644
--- a/navit/support/espeak/espeak-data/pap_dict
+++ b/navit/support/espeak/espeak-data/pap_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/phondata b/navit/support/espeak/espeak-data/phondata
index d46417ed0..2ccaf6817 100644
--- a/navit/support/espeak/espeak-data/phondata
+++ b/navit/support/espeak/espeak-data/phondata
Binary files differ
diff --git a/navit/support/espeak/espeak-data/phondata-manifest b/navit/support/espeak/espeak-data/phondata-manifest
index 4197d142d..f798b59bb 100644
--- a/navit/support/espeak/espeak-data/phondata-manifest
+++ b/navit/support/espeak/espeak-data/phondata-manifest
@@ -5,725 +5,837 @@
# S - A SPECT_SEQ structure
# W - A wavefile segment
# E - An envelope
+# Q - Phoneme equivalence tables
#
# Address is the displacement within phondata of this item
#
# Address Data file
# ------- ---------
-W 0x00004 ustop/null
-S 0x00154 vowel/@
-S 0x00218 vowel/@-
-W 0x002dc ustop/percus10
-S 0x004e8 j/j@
-S 0x005ec j2/j2@
-S 0x006b0 w/w@
-S 0x00774 l/l@
-S 0x00838 l^/j2@
-S 0x008fc r/r@
-S 0x009c0 r2/r2@
-S 0x00a84 m/m@
-S 0x00b88 n/n@
-S 0x00c8c nn/nn@
-S 0x00d50 n^/n^@
-S 0x00ed4 l/L1_@L
-S 0x00fd8 l/L2_@L
-S 0x010dc l/l_@
-S 0x011a0 l/xl
-S 0x01224 w/xw
-S 0x012a8 j/xj
-S 0x0132c r/xr
-S 0x013b0 r3/r_@
-S 0x01474 j/ja
-S 0x01578 j2/j2a
-S 0x0163c w/wa
-S 0x01700 l/la
-S 0x01784 l^/j2a
-S 0x01848 r/ra
-S 0x0190c r2/r2a
-S 0x019d0 m/ma
-S 0x01ad4 n/na
-S 0x01bd8 nn/nna
-S 0x01c9c n^/n^a
-S 0x01de0 l/L1_aL
-S 0x01ee4 l/L2_aL
-S 0x01fe8 l/l_a
-S 0x020ac r3/r_a
-S 0x02170 j/je
-S 0x02274 j2/j2e
-S 0x02338 w/we
-S 0x023fc l/le
-S 0x02480 l^/j2e
-S 0x02544 r/re
-S 0x02608 r2/r2e
-S 0x0270c m/me
-S 0x02810 n/ne
-S 0x02914 nn/nne
-S 0x029d8 n^/n^e
-S 0x02b5c l/L1_eL
-S 0x02c20 l/L2_eL
-S 0x02ce4 l/l_e
-S 0x02d68 r3/r_e
-S 0x02e2c j/ji
-S 0x02ef0 j2/j2i
-S 0x02fb4 w/wi
-S 0x03078 l/li
-S 0x0313c l^/j2i
-S 0x03200 r/ri
-S 0x032c4 r2/r2i
-S 0x03388 m/mi
-S 0x034cc n/ni
-S 0x035d0 nn/nni
-S 0x03694 n^/n^i
-S 0x03818 l/L1_iL
-S 0x0391c l/L2_iL
-S 0x03a20 l/l_i
-S 0x03ae4 nn/inn
-S 0x03ba8 j2/xj2
-S 0x03c2c r3/r_i
-S 0x03cf0 j/jo
-S 0x03e34 j2/j2o
-S 0x03f38 w/wo
-S 0x03ffc l/lo
-S 0x04100 l^/j2o
-S 0x04204 r/ro
-S 0x042c8 r2/r2o
-S 0x0438c m/mo
-S 0x04490 n/no
-S 0x04594 nn/nno
-S 0x04658 n^/n^o
-S 0x0481c l/L1_oL
-S 0x04960 l/L2_oL
-S 0x04aa4 l/l_o
-S 0x04ba8 r3/r_o
-S 0x04c6c j/ju
-S 0x04d30 j2/j2u
-S 0x04df4 w/wu
-S 0x04eb8 l/lu
-S 0x04f7c l^/j2u
-S 0x05080 r/ru
-S 0x05144 r2/r2u
-S 0x05208 m/mu
-S 0x0530c n/nu
-S 0x05410 nn/nnu
-S 0x054d4 n^/n^u
-S 0x05658 l/L1_uL
-S 0x0575c l/L2_uL
-S 0x05860 l/l_u
-S 0x05924 r3/r_u
-S 0x059e8 r/r
-S 0x05aac r/_r
-S 0x05b70 r/tr
-S 0x05bf4 r/r_
-S 0x05d38 r3/r_
-W 0x05dbc r3/rx
-S 0x07114 r3/r_n
-S 0x07198 r/rr
-S 0x0725c r/trr
-S 0x07320 r2/_r2
-S 0x073e4 r3/r_trill2
-W 0x074a8 r3/r_trill2.wav
-S 0x07bb4 r3/r_trill
-W 0x07cb8 r3/r_trill.wav
-W 0x08724 r3/r_trill3.wav
-S 0x08b68 r3/r_uvl
-W 0x08c6c r3/r_uvl.wav
-S 0x09b74 l/l
-S 0x09bf8 l/_l
-S 0x09cbc l/tl
-S 0x09d40 l/l_long
-S 0x09dc4 l/l_
-S 0x09e48 l^/l^
-S 0x09f4c l^/_l^
-S 0x0a050 w/w
-S 0x0a0d4 w/_w
-S 0x0a198 w/w_
-S 0x0a25c w/iw_
-S 0x0a320 j/_j
-S 0x0a3a4 j/j_
-S 0x0a468 j2/_j2
-S 0x0a4ec m/_m
-S 0x0a570 m/m_
-S 0x0a634 m/mj
-S 0x0a6f8 n/_n
-S 0x0a77c n/n_
-S 0x0a840 n/nj
-S 0x0a904 n/_nr
-S 0x0a988 n/nr_
-S 0x0aa4c n^/_n^
-S 0x0aad0 n^/n^_
-S 0x0ac14 nn/_nn
-S 0x0ac98 nn/nn_
-S 0x0ad5c nn/nnj
-S 0x0ade0 r3/@tap
-S 0x0af24 r3/@tap2
-S 0x0b068 r3/@tap_rfx
-S 0x0b1ac b/b
-W 0x0b2b0 x/b
-S 0x0b414 b/b_
-W 0x0b4d8 x/b_
-S 0x0b8f0 b/ba
-S 0x0b9f4 b/b@
-S 0x0baf8 b/be
-S 0x0bbfc b/bi
-S 0x0bd00 b/bo
-S 0x0be04 b/bu
-S 0x0bf08 b/b@2
-S 0x0c00c b/xb
-S 0x0c0d0 d/d
-W 0x0c194 x/d
-S 0x0c3b0 d/d_
-W 0x0c474 x/d_
-S 0x0c89c d/dr
-S 0x0c920 d/xd
-W 0x0c9e4 x/d_dnt
-S 0x0ccc8 d/tap3
-S 0x0cdcc d/tap1
-S 0x0ce90 dzh/dzh
-W 0x0cf54 x/dzh
-S 0x0d360 dzh/dzh_
-W 0x0d424 x/dzh_
-S 0x0de5c dzh/xdzh
-W 0x0df20 x/dz_pzd
-S 0x0e380 dzh/dz_pzd
-S 0x0e444 dzh/dz_pzd_
-S 0x0e508 dzh/xdz_pzd
-S 0x0e5cc g/g
-W 0x0e690 x/g2
-S 0x0e918 g/g_
-W 0x0e9dc x/g_
-S 0x0ed9c g/xg
-S 0x0ee60 g2/g
-W 0x0ef24 x/g2_
-S 0x0f2e4 g2/g_
-S 0x0f3a8 g2/xg
-S 0x0f46c voc/bh
-W 0x0f530 vocw/v
-S 0x0fe30 voc/v_
-S 0x0fef4 voc/v
-S 0x0fff8 voc/vj
-S 0x100fc voc/dh
-W 0x101c0 vocw/dh
-S 0x10ac8 voc/dh_
-S 0x10b8c voc/z
-W 0x10c50 ufric/s_
-S 0x11704 voc/z_
-S 0x117c8 voc/zh
-W 0x1188c vocw/zh
-S 0x121f4 voc/zh_
-W 0x122b8 vocw/zh_rfx
-S 0x12b48 voc/z_pzd
-W 0x12c0c ufric/s_pzd
-S 0x13544 voc/z_pzd_
-W 0x13608 ufric/s_pzd_
-W 0x1410c ufric/sh_pzd
-W 0x14a40 ufric/sh_pzd_
-S 0x15508 voc/j
-W 0x1560c ufric/ch
-S 0x15d24 voc/Q
-W 0x15de8 vocw/Q
-S 0x165f4 voc/Q_
-W 0x166b8 vocw/Q_
-S 0x16ec4 voc/Q_ulv
-W 0x16fc8 ufric/xx
-W 0x17ac4 ustop/p
-W 0x17e34 ustop/p_
-W 0x18644 ustop/pr
-W 0x18a8c ustop/p_unasp
-W 0x18db8 ustop/pl
-W 0x191b0 ustop/t
-W 0x195e4 ustop/t_
-W 0x19aa4 ustop/t_dnt
-W 0x19eb4 ustop/tr
-W 0x1a614 ustop/t_hi
-W 0x1a9b0 ustop/tsh
-W 0x1aff0 ustop/tsh_
-W 0x1b930 ustop/ts_pzd
-W 0x1c034 ustop/c
-W 0x1c2e4 ustop/t_pzd
-W 0x1c730 ustop/k
-W 0x1cc04 ustop/k_
-W 0x1d0c0 ustop/kr
-W 0x1d700 ustop/k_unasp
-W 0x1dbd4 ustop/kl
-W 0x1e204 ustop/ki
-W 0x1e7cc ustop/q
-W 0x1e938 ustop/q_u
-W 0x1ea58 ufric/f
-W 0x1f248 ufric/f_
-W 0x1fd18 ufric/th
-W 0x205b0 ufric/th_
-W 0x20e30 ufric/s
-W 0x215d0 ufric/s!
-W 0x21e80 ufric/sh
-W 0x22830 ufric/sh_
-W 0x232e0 ufric/sh_rfx
-W 0x23c7c ufric/ll
-W 0x246c0 ufric/ch_
-W 0x24fe8 ufric/x_hr
-W 0x258fc ufric/x
-W 0x26260 h/h_
-W 0x2690c h/h@
-W 0x26e60 h/ha
-W 0x274dc h/he
-W 0x27b70 h/hi
-W 0x28108 h/ho
-W 0x287c4 h/hu
-S 0x28ee8 vowel/a_2
-S 0x28fec vowel/a#
-S 0x290f0 vowel/e
-S 0x291f4 vowel/ee_1
-S 0x29338 vowel/i
-S 0x2943c vowel/o
-S 0x29580 vowel/oo_4
-S 0x29684 vowel/u_bck
-S 0x29788 vowel/uu_2
-S 0x2988c vowel/y
-S 0x299d0 vowel/y#
-S 0x29ad4 vdiph/au_4
-S 0x29c58 vdiph/eu
-S 0x29d9c vdiph2/iu
-S 0x29f20 vdiph/ai
-S 0x2a064 vdiph/ei
-S 0x2a1a8 vdiph/eei
-S 0x2a32c vdiph/oi
-S 0x2a4f0 vdiph/ui
-S 0x2a634 w/w2
-W 0x2a6f8 ustop/p_unasp_
-W 0x2a834 ustop/ts
-W 0x2b290 ustop/ts_
-W 0x2bbf0 ustop/t_dnt2
-S 0x2be48 vwl_en/@L
-S 0x2bf0c vowel/a
-S 0x2c010 vowel/a#_3
-S 0x2c0d4 vowel/ee_2
-S 0x2c1d8 vowel/ii_4
-S 0x2c2dc vowel/ii_en
-S 0x2c3a0 vowel/0
-S 0x2c4a4 vowel/V_2
-S 0x2c5a8 vowel/uu
-S 0x2c6ac vowel/aa_2
-S 0x2c830 vowel/3_en
-S 0x2c974 vowel/i_en
-S 0x2cab8 w/wi2
-S 0x2cbbc vowel/oo_en
-S 0x2cd00 vdiph2/uw_2
-S 0x2ce44 vwl_en/u_L
-S 0x2cf88 vdiph/au
-S 0x2d10c vdiph/@u_en
-S 0x2d290 vdiph/ai_2
-S 0x2d454 vdiph/ooi
-S 0x2d618 vdiph2/ii@
-S 0x2d79c vdiph2/uu@
-S 0x2d8e0 vwl_en/aI@
-S 0x2daa4 vwl_en/aU@
-S 0x2dc28 vowelr/V_r
-S 0x2dd6c vowelr/V3_r
-S 0x2deb0 vnasal/aa_n2
-S 0x2dff4 vnasal/ee_n
-S 0x2e138 vnasal/oo_n
-S 0x2e23c vowel/oe
-S 0x2e300 vowel/@_fr
-S 0x2e3c4 vowel/ee
-S 0x2e4c8 vowel/ii
-S 0x2e5cc vowel/e_3
-S 0x2e6d0 vowel/0_2
-S 0x2e7d4 vowel/o-_2
-S 0x2e8d8 vowel/aa_5
-S 0x2ea1c vwl_en_n/aa_5
-S 0x2eb60 vowel/3_2
-S 0x2eca4 vowel/oo_1
-S 0x2ede8 vwl_en_n/O@
-S 0x2eeec vdiph2/uw_4
-S 0x2f030 vdiph/eeu_3
-S 0x2f174 vdiph/ae_2
-S 0x2f2f8 vdiph2/ee@
-S 0x2f43c vdiph2/i@
-S 0x2f600 vwl_en_us/3_us
-S 0x2f704 vowel/@_4
-S 0x2f7c8 vowel/@_low2
-S 0x2f88c vwl_en_us/a
-S 0x2f990 vnasal/ee_n2
-S 0x2fad4 vwl_en_us/ee
-S 0x2fbd8 vowel/ii#_3
-S 0x2fcdc vowel/ii_final
-S 0x2fde0 vowel/aa_8
-S 0x2fee4 vwl_en_us/oor
-S 0x30028 vowel/V_6
-S 0x3012c vowel/8_2
-S 0x30230 vwl_en_us/ar
-S 0x30374 vwl_en_us/3_us2
-S 0x304b8 vowel/0_3
-S 0x305bc vwl_en_us/or
-S 0x30700 vowel/aa#
-S 0x30804 vdiph2/uw
-S 0x30948 vdiph/aoo
-S 0x30a8c vdiph/8u
-S 0x30bd0 vdiph/aae
-S 0x30d54 vdiph2/ei_4
-S 0x30e98 vdiph/ooi_4
-S 0x30fdc vwl_en_us/er
-S 0x31120 vwl_en_us/ir
-S 0x31264 vwl_en_us/ur
-S 0x313a8 vwl_en_us/ai@
-S 0x3152c d/tap2
-S 0x315f0 d/x_tap
-S 0x316b4 vowel/@_3
-S 0x31778 vowel/V
-S 0x3187c vowel/a_3
-S 0x31980 vowel/e_e
-S 0x31a84 vowel/e#
-S 0x31b88 vowel/e_5
-S 0x31c8c vowel/oo_2
-S 0x31d90 vowel/V_4
-S 0x31e94 vowel/u#_4
-S 0x31f98 vowelr/aa_r
-S 0x3215c vdiph2/e@
-S 0x322a0 vowel/i_5
-S 0x323a4 vowel/oo
-S 0x324a8 vowelr/oo_r
-S 0x325ac vowelr/o_r
-S 0x32730 vowel/u#
-S 0x32834 vdiph/au#
-S 0x32978 vowel/o_3
-S 0x32a7c vdiph/ai_7
-S 0x32c00 vwl_en/aI@_2
-S 0x32d84 vwl_en/@L_2
-S 0x32e88 vowel/e_2
-S 0x32f8c vdiph/0i_2
-S 0x33110 vowelr/i_r
-S 0x33254 vdiph2/u#@
-S 0x33398 vowel/@_low
-S 0x3345c vowel/&
-S 0x33560 vowel/e_mid
-S 0x33664 vowel/V_3
-S 0x33728 vowel/o-_3
-S 0x3382c vwl_en_rp/aa
-S 0x33970 vowel/3_3
-S 0x33ab4 vowel/u_fnt
-S 0x33bb8 vdiph/au_3
-S 0x33d3c vdiph/@u_2
-S 0x33e80 vdiph/ai_6
-S 0x34044 vdiph2/ei_2
-S 0x34188 vdiph/ooi_3
-S 0x3430c vdiph2/ee@_2
-S 0x34450 vwl_en_rp/i@
-S 0x345d4 vowel/o_mid
-S 0x346d8 vwl_en_rp/aU@
-S 0x3485c vowel/ii_6
-S 0x34920 vdiph2/ei_3
-S 0x34a64 vdiph/@u
-S 0x34ba8 vdiph/Vu_2
-S 0x34d2c vdiph/@i_3
-S 0x34e70 vdiph2/i@_2
-S 0x34ff4 vwl_en/ooi@
-S 0x351b8 vowel/@_fnt
-S 0x352bc vowel/uu_bck
-S 0x353c0 vowel/i_fnt
-S 0x354c4 vdiph2/o_oo
-S 0x35608 vowel/u
-S 0x3570c vdiph/aau_2
-S 0x35850 vdiph2/ie
-S 0x35994 vwl_af/@
-S 0x35a58 vwl_af/r@
-S 0x35b1c vowel/e_mid2
-S 0x35c20 vwl_af/I
-S 0x35ce4 vowel/oo_3
-S 0x35da8 vowel/uu_3
-S 0x35e6c vowel/ee_3
-S 0x35f30 l/L_eL_af
-S 0x35ff4 vowel/aa_3
-S 0x360f8 vdiph/i@_2
-S 0x3627c vowel/i_3
-S 0x36380 vdiph2/o@
-S 0x36504 vowel/y_3
-S 0x36608 vdiph2/iu_3
-S 0x367cc vdiph/Vu
-S 0x36950 vdiph/ai_4
-S 0x36ad4 vdiph/aai_2
-S 0x36c98 vdiph/@i_2
-S 0x36ddc vdiph/ooi_2
-S 0x36f60 vdiph/oi_2
-S 0x37124 vdiph/ui_2
-S 0x372a8 vdiph/y#y_2
-S 0x3742c vdiph2/y#@
-S 0x37570 vnasal/aa_n3
-S 0x376b4 vnasal/e_n
-S 0x377b8 vnasal/o_n2
-W 0x378fc ufric/x2
-S 0x38254 vowel/ii_3
-S 0x38358 vowel/ii#
-S 0x3845c vowel/i#
-S 0x38560 vowel/o_2
-S 0x386a4 vdiph2/iu_4
-S 0x387e8 vdiph/ui_3
-S 0x3896c vowel/aa_6
-S 0x38ab0 vowel/i_2
-S 0x38bb4 vdiph/ai_5
-S 0x38cf8 vowel/yy_4
-S 0x38dfc l/l_3
-S 0x38e80 j/_j_short
-S 0x38f04 vdiph/eei_2
-S 0x39048 vowelr/r-voc
-S 0x3918c vwl_hi/l-voc
-S 0x39290 vowel/i_4
-S 0x39394 vowel/aa_9
-S 0x39498 vowel/u_2
-S 0x3959c vowel/uu_4
-S 0x396a0 vdiph/aai_3
-S 0x39824 vdiph/&i
-S 0x39968 vdiph/y#i
-S 0x39aac vdiph/ui_4
-S 0x39bf0 vdiph/yi
-S 0x39d34 vdiph/aau
-S 0x39eb8 vdiph/ou
-S 0x39ffc vdiph/eu_2
-S 0x3a140 vdiph2/iu_2
-S 0x3a2c4 vdiph/&y
-S 0x3a408 vdiph/eey
-S 0x3a54c vdiph/y#y
-S 0x3a690 vdiph2/iy
-S 0x3a7d4 vdiph2/uo
-S 0x3a918 vdiph2/y-y#
-S 0x3aa5c r3/r_trill_short
-W 0x3ab60 ufric/s_continue
-W 0x3b310 h/hu_fi
-S 0x3bc00 vwl_fr/tr
-S 0x3bc84 vwl_fr/@R5
-S 0x3bd48 vowel/@_hgh
-S 0x3be0c vwl_fr/r_@
-S 0x3bed0 vowel/a_6
-S 0x3bfd4 vwl_fr/r_a
-S 0x3c058 vowel/e_8
-S 0x3c15c vwl_fr/r_e
-S 0x3c220 vwl_fr/r_i
-S 0x3c2e4 vwl_fr/r_o
-S 0x3c3a8 vowel/u_bck2
-S 0x3c4ac vwl_fr/r_u
-S 0x3c570 vowel/y_2
-S 0x3c6b4 l/l_y
-S 0x3c778 vwl_fr/r_y
-S 0x3c83c vowel/@_5
-S 0x3c900 vwl_fr/r_@2
-S 0x3c984 vwl_fr/w_a
-S 0x3cac8 vdiph/yi_fr
-S 0x3cc4c vnasal/aa_n4
-S 0x3cd90 vwl_fr/r_a~
-S 0x3ce14 vnasal/W_n
-S 0x3cf58 vowel/a_en
-S 0x3d05c vwl_fr/r
-S 0x3d0e0 r3/r_2
-W 0x3d164 ustop/t_short
-S 0x3d3a8 vowel/yy
-S 0x3d4ac vdiph/ae
-S 0x3d5f0 vowel/aa
-S 0x3d6f4 vwl_fr/@R2
-S 0x3d7f8 vowel/@_bck
-S 0x3d8fc vowel/i_6
-S 0x3da00 vdiph/ee-e
-S 0x3db44 vnasal/i_n2
-S 0x3dc48 vnasal/aa_n
-S 0x3dd8c vnasal/V_n
-S 0x3de90 vnasal/oo_n2
-S 0x3dfd4 vnasal/o_n
-S 0x3e118 vnasal/u_n
-S 0x3e21c vdiph/aau_3
-S 0x3e3a0 l^/l_rfx
-S 0x3e464 voc/v#
-S 0x3e568 voc/v#_
-W 0x3e62c ustop/p_asp
-S 0x3eb30 d/xd3
-W 0x3ebf4 ustop/ts_pzd2
-W 0x3ef28 ustop/ts_pzd_
-W 0x3f3ec ustop/k_asp
-S 0x3faf0 vowel/a_5
-S 0x3fbf4 vowel/u#_3
-S 0x3fcf8 vowel/u#_2
-S 0x3fdfc vowel/y#_2
-S 0x3ff00 vowel/8_7
-S 0x40004 vdiph/aai
-S 0x40188 vdiph2/uaa
-S 0x4030c vdiph2/ie_2
-W 0x40450 ustop/ts2
-S 0x40920 vowel/o_5
-S 0x40a24 vowel/o_6
-S 0x40ae8 vowel/aa_7
-S 0x40bec vdiph/y#y_3
-S 0x40cf0 vdiph/Vu_3
-S 0x40e74 vdiph2/yu
-S 0x40ff8 voc/Q_less
-W 0x410bc vocw/Q2
-W 0x41988 ufric/sx_sv
-S 0x421d0 vowel/a#_2
-S 0x422d4 vowel/ee#
-S 0x423d8 vowel/i_7
-S 0x424dc vowel/oo_5
-S 0x425e0 vowel/ii#_2
-S 0x426e4 vnasal/ee_u_n
-S 0x42868 vnasal/oo_n3
-W 0x429ec x/d_pzd
-S 0x42e00 d/xd_pzd
-S 0x42ec4 d/xdz
-S 0x42f88 vowel/ee_6
-S 0x4304c vdiph/ou_2
-S 0x43190 vdiph/eei_3
-W 0x432d4 r3/rz_cs
-S 0x43d80 voc/zh_2
-S 0x43e44 vdiph/oou
-W 0x43f88 ufric/sh3
-W 0x4491c ustop/tsh2
-W 0x45028 ustop/ts_pzd3
-S 0x456a8 dzh/dzh2
-W 0x4576c ustop/t_sr
-S 0x45aa4 d/d_dnt
-W 0x45b68 ufric/x_sr
-W 0x463e8 ufric/ch_sr
-W 0x46fc8 ufric/sh_pzd2
-W 0x47b18 ustop/ts_sr
-W 0x482d4 ustop/tsh_sr
-S 0x48bec vwl_ru/ii-
-S 0x48cb0 vwl_ro/mi
-S 0x48df4 vwl_ru/i
-S 0x48ef8 vwl_ru/ii#
-S 0x48fbc vwl_ru/i#
-S 0x490c0 vwl_ru/ii
-S 0x491c4 vwl_ru/e
-S 0x492c8 vwl_ru/E#
-S 0x493cc vwl_ru/E@
-S 0x494d0 vwl_ru/a
-S 0x495d4 vwl_ru/o
-S 0x496d8 vwl_ru/oo
-S 0x4979c vwl_ru/u
-S 0x498a0 vwl_ru/u#
-S 0x499e4 vwl_ru/u#u
-S 0x49b28 vwl_ru/8
-S 0x49bec vwl_ru/ee
-S 0x49d30 vwl_ru/ju
-S 0x49e34 vwl_ru/ja
-S 0x49fb8 vwl_ru/aa
-S 0x4a0bc r3/r_ru2
-W 0x4a1c0 r3/r_ru
-S 0x4a4c4 vowel/ii_5
-S 0x4a5c8 vdiph/eeu_2
-S 0x4a70c d/tap4
-S 0x4a810 voc/v2
-S 0x4a8d4 vnasal/i_n
-S 0x4a9d8 vnasal/a#_n
-S 0x4aadc vnasal/a#u_n
-S 0x4ac20 vnasal/oi_n
-S 0x4ade4 vdiph/0i
-S 0x4afa8 vdiph/eeu
-S 0x4b0ec vowel/i#_5
-S 0x4b1f0 vowel/u_6
-S 0x4b2f4 vwl_fr/@R
-S 0x4b3f8 vwl_ro/ii-
-S 0x4b4bc vwl_ro/li
-S 0x4b5c0 vwl_ro/ni
-S 0x4b6c4 vowel/o-_4
-S 0x4b7c8 vdiph/@u_3
-S 0x4b94c vdiph/ii
-S 0x4bb10 vdiph/i#i
-S 0x4bc54 vdiph2/uw_3
-S 0x4bd98 vdiph2/ea
-S 0x4bedc vdiph2/eo
-S 0x4c060 vdiph2/e[u
-S 0x4c1a4 vdiph2/oa
-S 0x4c2e8 d/tap
-S 0x4c3ac d/tap_i
-S 0x4c470 vowel/a_4
-S 0x4c574 vowel/ee#_2
-S 0x4c678 vowel/y_5
-S 0x4c7bc vowel/yy_3
-S 0x4c8c0 vowel/oe_4
-S 0x4c984 vowel/aa_4
-S 0x4ca88 vwl_sv/r_sv3
-S 0x4cc8c vowel/y_4
-S 0x4cd90 vowel/oe_2
-S 0x4ce94 vwl_no/y#
-S 0x4cf98 vwl_no/&
-S 0x4d09c vwl_no/u#
-S 0x4d1a0 vwl_no/u#2
-S 0x4d2e4 vdiph/ai_3
-S 0x4d428 vwl_no/y#y
-S 0x4d56c vwl_no/au-
-S 0x4d730 vowel/y##
-S 0x4d834 vowel/y#_3
-S 0x4d938 vdiph/ou_3
-S 0x4da3c vdiph/y#i_2
-S 0x4db80 m/m#_
-S 0x4dc84 n/n#_
-S 0x4dd88 n^/n^#_
-S 0x4de8c nn/nn#_
-W 0x4df90 ufric/tl#
-S 0x4e8f8 r3/r#_
-E 0x4e97c envelope/p_level
-E 0x4e9fc envelope/p_fall
-E 0x4ea7c envelope/p_rise
-E 0x4eafc envelope/p_fallrise
-E 0x4eb7c envelope/p_214
-E 0x4ebfc envelope/vi_5amp
-E 0x4ec7c envelope/p_512
-E 0x4ecfc envelope/vi_6amp
-S 0x4ed7c vowel/u_7
-S 0x4ee80 vowel/u#_5
-S 0x4ef84 vowel/@_2
-S 0x4f088 vdiph/&i_3
-S 0x4f20c vdiph/@i
-S 0x4f350 vdiph/u-i
-S 0x4f494 vdiph/aau_4
-S 0x4f618 vdiph2/ii@_3
-S 0x4f79c l/l_vi
-S 0x4f8a0 vwl_zh/ang
-S 0x4faa4 vwl_zh/aang
-S 0x4fce8 vdiph/au_2
-S 0x4fe6c vwl_zh/eng
-S 0x50070 vwl_zh/ing
-S 0x502b4 vwl_zh/ng
-S 0x503f8 vwl_zh/oeng
-S 0x505bc vwl_zh/ong
-S 0x50740 vwl_zh/ung
-S 0x508c4 vowel/8_3
-E 0x509c8 envelope/i_risefall
-W 0x50a48 ustop/t_unasp2
-S 0x50b54 n/n_long_
-W 0x50c18 ustop/k_unasp_
-W 0x50ed4 ustop/tsh_pzd_unasp
-W 0x51724 ustop/tsh_pzd
-W 0x52264 ustop/ts_unasp
-W 0x52adc ustop/ts_rfx_unasp
-W 0x536c8 ustop/ts_rfx
-S 0x543a8 nn/nn2_
-S 0x5446c vwl_zh/a_n
-S 0x54570 vwl_zh/aau
-S 0x546b4 vowel/ii_2
-S 0x54778 vowel/i#_7
-S 0x5487c vowel/i#_6
-S 0x54980 vwl_zh/iaa
-S 0x54ac4 vwl_zh/iaau
-S 0x54c48 vwl_zh/ie
-S 0x54d8c vdiph2/iioo
-S 0x54ed0 vwl_zh/iou
-S 0x55054 vowel/8
-S 0x55158 vwl_zh/uaa
-S 0x5529c vwl_zh/uai
-S 0x55420 vwl_zh/uei
-S 0x555a4 vwl_zh/uo
-S 0x55728 vwl_zh/y&
-S 0x5586c vwl_zh/yee
-S 0x559b0 vdiph2/y@
-S 0x55af4 vowel/u_5
-S 0x55bf8 vnasal/m-
-S 0x55cfc vnasal/n-
-S 0x55e00 vnasal/nn-
-S 0x55f04 vowel/u#_7
-S 0x56008 vowel/8_5
-S 0x5610c vowel/o_7
-S 0x56210 vowel/uu#
-S 0x56314 vowel/8_6
-S 0x56418 vowel/ee_4
-S 0x5651c vdiph2/ye
-S 0x56660 l/l_front_
-S 0x567a4 l/l_front
-S 0x568a8 l/l_4
-S 0x5696c vwl_fr/@R3
-S 0x56a30 vwl_fr/@R4
-S 0x56af4 r/a_
+W 0x00008 ustop/null
+S 0x00158 vowel/@
+S 0x0021c vowel/@-
+S 0x002e0 vowel/a
+S 0x003e4 vowel/e
+S 0x004e8 vowel/i
+S 0x005ec vowel/oo
+S 0x006f0 vowel/u
+S 0x007f4 klatt/m-syl
+S 0x00978 m/m-syl
+S 0x00afc klatt/n-syl
+S 0x00c40 n/n-syl
+S 0x00d84 nn/nn-syl
+W 0x00ec8 ustop/percus10
+S 0x010d4 vowelr/r-voc
+S 0x01218 vwl_hi/l-voc
+S 0x0131c r/r@
+S 0x013e0 r/ra
+S 0x014a4 r/re
+S 0x01568 r/ri
+S 0x0162c r/ro
+S 0x016f0 r/ru
+S 0x017b4 r/xr
+S 0x01838 r/_r
+S 0x018fc r/tr
+S 0x01980 r/r
+S 0x01a44 r3/r_n
+W 0x01ac8 r3/rx
+S 0x02e20 r/trr
+S 0x02ee4 r/rr
+S 0x02fa8 r3/r_
+S 0x0302c r3/r_trill2
+W 0x030f0 r3/r_trill2.wav
+S 0x037fc r3/r_trill
+W 0x03900 r3/r_trill.wav
+W 0x0436c r3/r_trill3.wav
+S 0x047b0 r3/r_uvl
+W 0x048b4 r3/r_uvl.wav
+S 0x057bc l/l@
+S 0x05880 l/la
+S 0x05904 l/le
+S 0x05988 l/li
+S 0x05a4c l/lo
+S 0x05b50 l/lu
+S 0x05c14 l/L1_@L
+S 0x05d18 l/L1_aL
+S 0x05e1c l/L1_eL
+S 0x05ee0 l/L1_iL
+S 0x05fe4 l/L1_oL
+S 0x06128 l/L1_uL
+S 0x0622c l/l_
+S 0x062b0 l/xl
+S 0x06334 l/_l
+S 0x063f8 l/tl
+S 0x0647c l/l_long
+S 0x06500 l/l
+S 0x06584 l/L2_eL
+S 0x06648 l/L2_uL
+S 0x0674c l/L2_@L
+S 0x06850 l/L2_aL
+S 0x06954 l/L2_iL
+S 0x06a58 l/L2_oL
+S 0x06b9c l/l_@
+S 0x06c20 l/l_a
+S 0x06ca4 l/l_e
+S 0x06d28 l/l_i
+S 0x06dec l/l_o
+S 0x06eb0 l/l_u
+S 0x06f34 l^/j2@
+S 0x06ff8 l^/j2a
+S 0x070bc l^/j2e
+S 0x07180 l^/j2i
+S 0x07244 l^/j2o
+S 0x07348 l^/j2u
+S 0x0744c l^/_l^
+S 0x07550 l^/l^
+S 0x07654 l^/l_rfx
+S 0x07718 ll/xll
+S 0x077dc ll/_ll
+S 0x078e0 ll/ll
+S 0x079a4 w/w@
+S 0x07a68 w/wa
+S 0x07b2c w/we
+S 0x07bf0 w/wi
+S 0x07cb4 w/wo
+S 0x07d78 w/wu
+S 0x07e3c w/xw
+S 0x07ec0 w/w
+S 0x07f44 w/_w
+S 0x08008 w/iw_
+S 0x0810c w/w_
+S 0x081d0 j/j@
+S 0x082d4 j/ja
+S 0x083d8 j/je
+S 0x084dc j/ji
+S 0x085a0 j/jo
+S 0x086e4 j/ju
+S 0x087a8 j/xj
+S 0x0882c j/_j
+S 0x088b0 j/j_
+S 0x08974 j2/j2@
+S 0x08a38 j2/j2a
+S 0x08afc j2/j2e
+S 0x08bc0 j2/j2i
+S 0x08c84 j2/j2o
+S 0x08d88 j2/j2u
+S 0x08e4c j2/xj2
+S 0x08ed0 j2/_j2
+S 0x08f54 klatt/m_
+S 0x09058 klatt/m
+S 0x0915c m/m@
+S 0x09260 m/ma
+S 0x09364 m/me
+S 0x09468 m/mi
+S 0x095ac m/mo
+S 0x096b0 m/mu
+S 0x097b4 m/mj
+S 0x09878 m/_m
+S 0x098fc m/m_
+S 0x099c0 klatt/n
+S 0x09ac4 n/n@
+S 0x09bc8 n/na
+S 0x09ccc n/ne
+S 0x09dd0 n/ni
+S 0x09ed4 n/no
+S 0x09fd8 n/nu
+S 0x0a0dc n/nj
+S 0x0a1a0 n/_n
+S 0x0a224 n/n_
+S 0x0a2e8 klatt/nr
+S 0x0a3ec n/nr@
+S 0x0a4f0 n/nra
+S 0x0a5f4 n/nre
+S 0x0a6f8 n/nri
+S 0x0a7fc n/nro
+S 0x0a900 n/nru
+S 0x0aa04 n/_nr
+S 0x0aa88 n/nr_
+S 0x0ab4c klatt/n^@
+S 0x0abd0 klatt/n^
+S 0x0acd4 n^/n^@
+S 0x0ae58 n^/n^a
+S 0x0af9c n^/n^e
+S 0x0b120 n^/n^i
+S 0x0b2a4 n^/n^o
+S 0x0b468 n^/n^u
+S 0x0b5ec n^/_n^
+S 0x0b670 n^/n^_
+S 0x0b7b4 klatt/nn_
+S 0x0b8b8 klatt/nn
+S 0x0b9bc nn/nn@
+S 0x0ba80 nn/nna
+S 0x0bb44 nn/nne
+S 0x0bc08 nn/nni
+S 0x0bccc nn/nno
+S 0x0bd90 nn/nnu
+S 0x0be54 nn/inn
+S 0x0bf18 nn/nnj
+S 0x0bf9c nn/_nn
+S 0x0c020 nn/nn_
+S 0x0c0e4 r3/@tap2
+S 0x0c228 r3/@tap
+S 0x0c36c d/tap1
+S 0x0c430 d/tap3
+S 0x0c534 d/dr
+S 0x0c5b8 r3/@tap_rfx_
+S 0x0c6fc r3/@tap_rfx
+S 0x0c840 b/xb
+S 0x0c904 klatt/b
+W 0x0c9c8 x/b_
+W 0x0cde0 x/b
+S 0x0cf44 b/b_
+S 0x0d008 b/b@2
+S 0x0d10c b/b@
+S 0x0d210 b/ba
+S 0x0d314 b/be
+S 0x0d418 b/bi
+S 0x0d51c b/bo
+S 0x0d620 b/bu
+S 0x0d724 b/b
+S 0x0d828 d/xd
+S 0x0d8ec d/d_
+W 0x0d9b0 x/d_
+S 0x0ddd8 d/d
+W 0x0de9c x/d
+W 0x0e0b8 x/d_dnt
+S 0x0e39c dzh/xdzh
+S 0x0e460 dzh/dzh_
+W 0x0e524 x/dzh_
+S 0x0ef5c dzh/dzh
+W 0x0f020 x/dzh
+W 0x0f42c x/dz_pzd
+S 0x0f88c dzh/xdz_pzd
+S 0x0f950 klatt/dz_pzd_
+S 0x0fa14 klatt/dz_pzd
+S 0x0fad8 dzh/dz_pzd_
+S 0x0fb9c dzh/dz_pzd
+S 0x0fc60 g/xg
+S 0x0fd24 g/g_
+W 0x0fde8 x/g_
+S 0x101a8 g/g
+W 0x1026c x/g2
+S 0x104f4 klatt/v_
+W 0x105b8 vocw/v
+S 0x10eb8 klatt/bh
+S 0x10f7c voc/v_
+S 0x11040 voc/bh
+S 0x11104 klatt/v
+S 0x111c8 voc/v
+S 0x112cc voc/v#_
+S 0x11390 voc/v#
+S 0x11494 voc/dh_
+W 0x11558 vocw/dh
+S 0x11e60 voc/dh
+S 0x11f24 voc/z_
+W 0x11fe8 ufric/s_
+S 0x12a9c voc/z
+S 0x12b60 klatt/zh_
+W 0x12c24 vocw/zh
+S 0x1358c klatt/zh
+S 0x13650 voc/zh_
+S 0x13714 voc/zh
+W 0x137d8 vocw/zh_rfx
+S 0x14068 voc/z_pzd_
+W 0x1412c ufric/s_pzd_
+S 0x14c30 voc/z_pzd
+W 0x14cf4 ufric/s_pzd
+W 0x1562c ufric/sh_pzd_
+W 0x160f4 ufric/sh_pzd
+S 0x16a28 voc/j
+W 0x16b2c ufric/ch
+S 0x17244 klatt/qqh_
+W 0x17308 vocw/Q_
+S 0x17b14 klatt/qqh
+W 0x17bd8 vocw/Q
+S 0x183e4 voc/Q_
+S 0x184a8 voc/Q
+S 0x1856c voc/Q_ulv
+W 0x18670 ufric/xx
+W 0x1916c ustop/p_
+W 0x1997c ustop/pr
+W 0x19dc4 ustop/p_unasp
+W 0x1a0f0 ustop/pl
+W 0x1a4e8 ustop/p
+W 0x1a858 ustop/t_
+W 0x1ad18 ustop/tr
+W 0x1b478 ustop/t_dnt
+W 0x1b888 ustop/t
+W 0x1bcbc ustop/t_hi
+W 0x1bf88 ustop/tsh_
+W 0x1c8c8 ustop/tsh
+W 0x1cf08 ustop/ts_pzd
+W 0x1d60c ustop/t_pzd
+W 0x1da58 ustop/c
+W 0x1dd08 ustop/k_
+W 0x1e1c4 ustop/kr
+W 0x1e804 ustop/ki
+W 0x1edcc ustop/kl
+W 0x1f3fc ustop/k_unasp
+W 0x1f8d0 ustop/k
+W 0x1fda4 ustop/q
+W 0x1ff10 ustop/q_u
+W 0x20030 ufric/f_
+W 0x20b00 ufric/f
+W 0x212f0 ufric/th_
+W 0x21b70 ufric/th
+W 0x22408 ufric/s!
+W 0x22cb8 ufric/s
+W 0x23458 ufric/sh_
+W 0x23f08 ufric/sh
+W 0x248b8 ufric/sh_rfx
+W 0x25254 ufric/l#
+W 0x25c98 ufric/ch_
+W 0x265c0 ufric/x_
+W 0x2711c ufric/x_hr
+W 0x27a30 h/h@
+W 0x27f84 h/ha
+W 0x28600 h/he
+W 0x28c94 h/hi
+W 0x2922c h/ho
+W 0x298e8 h/hu
+W 0x2a00c h/h_
+W 0x2a6b8 ustop/ts_
+W 0x2b018 ustop/ts
+S 0x2ba74 d/xdz
+W 0x2bb38 ustop/p_unasp_
+W 0x2bc74 ustop/p_asp
+W 0x2c178 ustop/t_short
+W 0x2c3bc ustop/ts_pzd_
+W 0x2c880 ustop/ts_pzd2
+W 0x2cbb4 ustop/k_asp
+W 0x2d2b8 ustop/k_asp_e
+W 0x2da20 ustop/k_asp_a
+W 0x2e2a4 ufric/s_continue
+S 0x2ea54 vowel/a#
+S 0x2eb58 vowel/a_2
+S 0x2ec5c vowel/ee_1
+S 0x2eda0 vowel/o
+S 0x2eee4 vowel/oo_4
+S 0x2efe8 vowel/u_bck
+S 0x2f0ec vowel/uu_2
+S 0x2f1f0 vowel/y
+S 0x2f334 vowel/y#
+S 0x2f438 vdiph/au_4
+S 0x2f5bc vdiph/eu
+S 0x2f700 vdiph2/iu
+S 0x2f884 vdiph/ai
+S 0x2f9c8 vdiph/ei
+S 0x2fb0c vdiph/eei
+S 0x2fc90 vdiph/oi
+S 0x2fe54 vdiph/ui
+S 0x2ff98 w/w2
+W 0x3005c ustop/t_dnt2
+S 0x302b4 klatt/x_tap
+S 0x30378 klatt/tap2
+S 0x3043c d/x_tap
+S 0x30500 d/tap2
+W 0x305c4 x/g2_
+S 0x30984 r2/r2@
+S 0x30a48 r2/r2a
+S 0x30b0c r2/r2e
+S 0x30c10 r2/r2i
+S 0x30cd4 r2/r2o
+S 0x30d98 r2/r2u
+S 0x30e5c vowel/@_6
+S 0x30f60 vwl_en/@L
+S 0x31024 vowel/a_8
+S 0x31128 vowel/a#_3
+S 0x3122c vowel/ee_5
+S 0x31330 vowel/ii_2
+S 0x31434 vowel/ii_4
+S 0x31538 vowel/ii_7
+S 0x3163c vowel/0
+S 0x31740 vowel/V_2
+S 0x31844 vowel/uu
+S 0x31948 vowel/aa_2
+S 0x31acc vowel/3_en
+S 0x31c10 w/wi2
+S 0x31d14 vowel/i_en
+S 0x31e58 vowel/oo_en
+S 0x31f9c vwl_en/u_L
+S 0x320e0 vdiph2/uw_2
+S 0x32224 vdiph/au
+S 0x323a8 vdiph/@u_en
+S 0x3252c vdiph/ai_2
+S 0x326f0 vdiph/ooi
+S 0x328b4 vdiph2/ii@
+S 0x32a38 vdiph2/8@
+S 0x32b7c vdiph2/uu@
+S 0x32d00 vwl_en/aI@
+S 0x32ec4 vwl_en/aU@
+S 0x33048 vowelr/V_r
+S 0x3318c vowelr/V3_r
+S 0x332d0 vnasal/aa_n2
+S 0x33414 vnasal/oo_n2
+S 0x33558 vowel/@_3
+S 0x3361c vowel/@_fr
+S 0x336e0 vowel/ee
+S 0x337e4 vowel/ii
+S 0x338e8 vowel/e_2
+S 0x339ec vowel/0_2
+S 0x33af0 vowel/o-_2
+S 0x33bf4 vowel/aa_5
+S 0x33d38 vowel/3_2
+S 0x33e7c vowel/oo_1
+S 0x33fc0 vwl_en_n/O@
+S 0x340c4 vdiph2/uw_4
+S 0x34208 vdiph/eeu_3
+S 0x3434c vdiph/ae_2
+S 0x344d0 vdiph2/ee@
+S 0x34614 vdiph2/i@
+S 0x347d8 vwl_en_us/3_us
+S 0x348dc vowel/@_4
+S 0x349a0 vowel/@_low2
+S 0x34a64 vnasal/ee_n2
+S 0x34ba8 vwl_en_us/a
+S 0x34cac vwl_en_us/ee
+S 0x34db0 vowel/ii#_3
+S 0x34eb4 vowel/ii#
+S 0x34fb8 vowel/ii_final
+S 0x350bc vowel/aa_8
+S 0x351c0 vowel/V_6
+S 0x352c4 vowel/8_2
+S 0x353c8 vwl_en_us/ar
+S 0x3554c vwl_en_us/3_us2
+S 0x35690 vwl_en_us/oor
+S 0x35814 vowel/0_3
+S 0x35918 vwl_en_us/or
+S 0x35a5c vowel/aa#
+S 0x35b60 vdiph2/uw_6
+S 0x35ca4 vdiph/aoo
+S 0x35de8 vdiph/8u
+S 0x35f2c vdiph/aae
+S 0x360b0 vdiph2/ei_4
+S 0x361f4 vdiph/ooi_3
+S 0x36378 vwl_en_us/er
+S 0x364fc vwl_en_us/ir
+S 0x36680 vwl_en_us/ur
+S 0x367c4 vwl_en_us/ai@
+S 0x36948 vwl_en_us/ai3
+S 0x36b0c vwl_en_us/aU@
+S 0x36cd0 vowel/V
+S 0x36dd4 vowel/a_3
+S 0x36ed8 vowel/e_e
+S 0x36fdc vowel/e#
+S 0x370e0 vowel/e_5
+S 0x371e4 vowel/oo_2
+S 0x372e8 vowel/V_4
+S 0x373ec vowel/u#_4
+S 0x374f0 vowelr/aa_r
+S 0x376b4 vowelr/e_r
+S 0x37838 vowel/i_5
+S 0x3793c vowelr/oo_r
+S 0x37a40 vowel/u#
+S 0x37b44 vdiph/au#
+S 0x37c88 vowel/o_3
+S 0x37dcc vwl_en/aI@_2
+S 0x37f50 vdiph/ai_7
+S 0x380d4 vdiph/0i_2
+S 0x38258 vdiph2/e@
+S 0x3839c vowelr/i_r
+S 0x384e0 vdiph2/u#@
+S 0x38624 vwl_en/@L_2
+S 0x38728 vowel/@_low
+S 0x387ec vowel/&
+S 0x388f0 vowel/e_mid
+S 0x389f4 vowel/V_3
+S 0x38af8 vowel/o-_3
+S 0x38bfc vwl_en_rp/aa
+S 0x38d40 vowel/3_3
+S 0x38e84 vowel/uu#_2
+S 0x38f88 vdiph/au_3
+S 0x3910c vdiph/@u_2
+S 0x39250 vdiph/ai_6
+S 0x39414 vdiph2/ei_2
+S 0x39558 vdiph2/ee@_2
+S 0x3969c vwl_en_rp/i@
+S 0x39820 vowel/o_mid
+S 0x39924 vwl_en_rp/aU@
+S 0x39aa8 vowel/ii_6
+S 0x39b6c vdiph2/ei_3
+S 0x39cb0 vdiph/@u
+S 0x39df4 vdiph/Vu_2
+S 0x39f78 vdiph/@i_3
+S 0x3a0bc vdiph2/i@_2
+S 0x3a240 vwl_en/ooi@
+S 0x3a404 vowel/@_fnt
+S 0x3a508 vowel/uu_bck
+S 0x3a60c vowel/i_fnt
+S 0x3a710 vdiph2/o_oo
+S 0x3a854 vdiph/aau_2
+S 0x3a998 vdiph2/ie
+S 0x3aadc vwl_af/r@
+S 0x3aba0 vwl_af/@
+S 0x3ac64 vowel/e_mid2
+S 0x3ad68 vwl_af/I
+S 0x3ae2c vowel/oo_3
+S 0x3aef0 vowel/oe
+S 0x3aff4 vowel/uu_3
+S 0x3b0b8 l/L_eL_af
+S 0x3b17c vowel/ee_3
+S 0x3b240 vowel/aa_3
+S 0x3b344 vdiph/i@_2
+S 0x3b4c8 vowel/i_3
+S 0x3b5cc vdiph2/o@
+S 0x3b750 vowel/y_3
+S 0x3b854 vdiph2/iu_3
+S 0x3ba18 vdiph/Vu
+S 0x3bb9c vdiph/ai_4
+S 0x3bd20 vdiph/aai_2
+S 0x3bee4 vdiph/@i_2
+S 0x3c028 vdiph/ooi_2
+S 0x3c1ec vdiph/oi_2
+S 0x3c3b0 vdiph/ui_2
+S 0x3c534 vdiph/y#y_2
+S 0x3c6b8 vdiph2/y#@
+S 0x3c7fc vnasal/aa_n3
+S 0x3c940 vnasal/e_n
+S 0x3ca44 vnasal/o_n2
+W 0x3cb88 ufric/x2
+S 0x3d4e0 vowel/ee_2
+S 0x3d5e4 vowel/ii_3
+S 0x3d6e8 vowel/i#
+S 0x3d7ec vowel/o_2
+S 0x3d930 vdiph2/iu_4
+S 0x3da74 vdiph/ui_3
+S 0x3dbf8 vowel/a_4
+S 0x3dcfc vdiph/@u_3
+S 0x3de80 vdiph2/u@
+S 0x3e004 vowel/aa_6
+S 0x3e148 vowel/i_2
+S 0x3e24c vdiph/aau_6
+S 0x3e3d0 vdiph/ai_5
+S 0x3e514 vowel/e_8
+S 0x3e618 vowel/yy_4
+S 0x3e71c l/l_3
+S 0x3e7a0 j/_j_short
+S 0x3e824 vdiph2/i@_3
+S 0x3e9a8 vwl_de/uu_@
+S 0x3eaec vnasal/aa_n4
+Q 0x3ec30 de
+S 0x3ec40 vdiph/eei_2
+S 0x3ed84 vowel/i_4
+S 0x3ee88 vowel/aa_9
+S 0x3ef8c vowel/u_2
+S 0x3f090 vowel/uu_4
+S 0x3f194 vdiph/aai_3
+S 0x3f318 vdiph/&i
+S 0x3f45c vdiph/y#i
+S 0x3f5a0 vdiph/ui_4
+S 0x3f6e4 vdiph/yi
+S 0x3f828 vdiph/aau
+S 0x3f9ac vdiph/ou
+S 0x3faf0 vdiph/eu_2
+S 0x3fc34 vdiph2/iu_2
+S 0x3fdb8 vdiph/&y
+S 0x3fefc vdiph/eey
+S 0x40040 vdiph/y#y
+S 0x40184 vdiph2/iy
+S 0x402c8 vdiph2/uo
+S 0x4040c vdiph2/y-y#
+S 0x40550 r3/r_trill_short
+W 0x40654 h/hu_fi
+S 0x40f44 vowel/aa
+S 0x41048 vowel/e_7
+S 0x4114c vowel/ee#_2
+S 0x41250 vowel/i_8
+S 0x41354 vowel/i_7
+S 0x41458 vowel/i#_7
+S 0x4155c vowel/u_bck2
+S 0x41660 vowel/u_bck3
+S 0x41764 vowel/u_5
+S 0x41868 vowel/8_7
+S 0x4196c vowel/8_4
+S 0x41a70 vdiph/@i
+W 0x41bb4 ufric/s_pal
+S 0x42468 d/xd_pzd
+W 0x4252c x/d_pzd
+S 0x42940 vwl_fr/y2r
+S 0x429c4 vwl_fr/e_2r
+S 0x42a48 vwl_fr/aa2r
+S 0x42acc vwl_fr/ee2r
+S 0x42b90 vwl_fr/oo2r
+S 0x42c54 vwl_fr/@2r
+S 0x42cd8 vwl_fr/a2r
+S 0x42d5c vwl_fr/e2r
+S 0x42de0 vwl_fr/i2r
+S 0x42e64 vwl_fr/o2r
+S 0x42ee8 vwl_fr/u2r
+S 0x42f6c vwl_fr/re2
+S 0x42ff0 vwl_fr/r@2
+S 0x43074 vwl_fr/raa
+S 0x430f8 vwl_fr/ree
+S 0x4317c vwl_fr/ry
+S 0x43200 vwl_fr/rw
+S 0x43284 vwl_fr/roo
+S 0x43308 vwl_fr/rj
+S 0x4338c vwl_fr/r@
+S 0x43410 vwl_fr/ra
+S 0x43494 vwl_fr/re
+S 0x43518 vwl_fr/ri
+S 0x4359c vwl_fr/ro
+S 0x43620 vwl_fr/ru
+S 0x436a4 vwl_fr/r
+S 0x43728 vwl_fr/trr
+S 0x437ec vwl_fr/rr
+S 0x43870 vwl_fr/r_@
+S 0x438f4 vwl_fr/r_a
+S 0x43978 vwl_fr/r_e
+S 0x439fc vwl_fr/r_i
+S 0x43a80 vwl_fr/r_o
+S 0x43b04 vwl_fr/r_u
+S 0x43b88 vwl_fr/r_y
+S 0x43c0c vwl_fr/r_n
+S 0x43cd0 vwl_fr/r_
+S 0x43d54 vwl_fr/tr
+S 0x43e58 vwl_fr/br
+S 0x43f5c vwl_fr/lo
+S 0x43fe0 l/l_y
+S 0x44064 vowel/@_hgh
+S 0x44128 vowel/a_7
+S 0x4422c vwl_fr/j
+S 0x44330 vowel/o_8
+S 0x44474 vowel/o_mid2
+S 0x44578 vwl_fr/wa
+S 0x4463c vnasal/W_n
+S 0x44780 vnasal/o_n4
+S 0x448c4 b/xbr
+S 0x44988 b/br
+S 0x44a0c d/xdr
+S 0x44ad0 g/xgr
+S 0x44b94 g/gr
+W 0x44c58 x/g
+S 0x44f18 n/n_long_
+W 0x44fdc ustop/t_short_
+Q 0x45268 fr
+S 0x4531c vnasal/ee_n
+S 0x45460 vowel/yy
+S 0x45564 vdiph/ae
+S 0x456a8 vwl_fr/@R2
+S 0x457ac vowel/o_6
+S 0x458b0 vowel/a#_2
+S 0x459b4 vowel/a#_4
+S 0x45ab8 vdiph/y#y_3
+S 0x45bbc vdiph/ou_4
+S 0x45d00 vdiph2/yu
+S 0x45e84 voc/Q_less
+W 0x45f48 vocw/Q2
+S 0x46814 vowel/@_bck
+S 0x46918 vdiph/ee-e
+S 0x46a5c vowel/a_5
+S 0x46b60 vnasal/i_n2
+S 0x46c64 vnasal/ii_n
+S 0x46d68 vnasal/&_n
+S 0x46e6c vnasal/V_n
+S 0x46f70 vnasal/o_n
+S 0x470b4 vnasal/u_n
+S 0x471b8 vdiph/aau_3
+S 0x4733c d/xd3
+W 0x47400 ustop/th_rfx2
+W 0x47a04 ustop/tsh_unasp
+S 0x47ec4 g2/xg
+W 0x47f88 ustop/percus02
+S 0x482b8 vowel/i_6
+S 0x483bc vowel/&_2
+S 0x484c0 vowel/oo_5
+W 0x485c4 ustop/tsh_unasp2
+S 0x48a0c vdiph/aai
+S 0x48b90 vdiph/Vi
+E 0x48d14 envelope/p_512
+S 0x48d94 vowel/ii_5
+S 0x48e98 vowel/u_6
+S 0x48f9c vowel/u#_3
+S 0x490a0 vdiph/ai_8
+S 0x49224 voc/murmur1
+S 0x49368 vowel/y#_2
+S 0x4946c vowel/e_3
+S 0x49570 vwl_ru/ee
+S 0x496b4 vdiph2/ea
+S 0x497f8 vdiph2/uaa
+S 0x4997c vdiph2/ie_2
+S 0x49ac0 vdiph/ou_2
+S 0x49c04 vdiph/eei_3
+W 0x49d48 ustop/ts2
+S 0x4a218 vowel/ee#
+S 0x4a31c vowel/ii#_2
+S 0x4a420 vnasal/ee_u_n
+S 0x4a5a4 vnasal/oo_n3
+S 0x4a728 vowel/ee_6
+W 0x4a7ec r3/rz_cs
+S 0x4b298 voc/zh_2
+S 0x4b35c vdiph/oou
+W 0x4b4a0 ufric/sh3
+W 0x4be34 ustop/tsh2
+W 0x4c540 ustop/ts_pzd3
+S 0x4cbc0 dzh/dzh2
+W 0x4cc84 ustop/t_sr
+S 0x4cfbc d/d_dnt
+W 0x4d080 ufric/ch_sr
+W 0x4dc60 ufric/x_sr
+W 0x4e4e0 ufric/sh_pzd2
+W 0x4f030 ustop/ts_sr
+W 0x4f7ec ustop/tsh_sr
+W 0x50104 x/g3
+S 0x502ec vwl_ro/mi
+S 0x50430 vwl_ru/ii-
+S 0x504f4 vwl_ru/ii
+S 0x505f8 vwl_ru/ii#
+S 0x506bc vwl_ru/i#
+S 0x507c0 vwl_ru/e
+S 0x508c4 vwl_ru/E#
+S 0x509c8 vwl_ru/E@
+S 0x50acc vwl_ru/o
+S 0x50bd0 vwl_ru/oo
+S 0x50c94 vwl_ru/u
+S 0x50d98 vwl_ru/u#
+S 0x50edc vwl_ru/u#u
+S 0x51020 vwl_ru/8
+S 0x510e4 vwl_ru/ju
+S 0x511e8 vwl_ru/ja
+S 0x5136c vwl_ru/a
+S 0x51470 vwl_ru/aa
+S 0x51574 r3/r_ru2
+W 0x51678 r3/r_ru
+S 0x5197c vowel/y_2
+S 0x51ac0 vdiph/eeu_2
+S 0x51c04 voc/v2
+S 0x51cc8 vowel/a_6
+S 0x51dcc vnasal/i_n
+S 0x51ed0 vnasal/a#_n2
+S 0x51fd4 vnasal/a#_n
+S 0x520d8 vnasal/a#u_n
+S 0x5221c vnasal/oi_n
+S 0x523e0 vdiph/0i
+S 0x525a4 vdiph/eeu
+S 0x526e8 vowel/i#_5
+S 0x527ec vwl_fr/@R
+S 0x528f0 vwl_ro/li
+S 0x529f4 vwl_ro/ni
+S 0x52af8 vwl_ro/ii-
+S 0x52bbc vowel/o-_4
+S 0x52cc0 vdiph/ii
+S 0x52e84 vdiph/i#i
+S 0x52fc8 vdiph2/uw_3
+S 0x5310c vdiph2/eo
+S 0x53290 vdiph2/e_u
+S 0x533d4 vdiph2/oa
+S 0x53518 d/tap_i
+S 0x535dc d/tap
+W 0x536a0 ustop/t_unasp2
+S 0x537ac vowel/y_5
+S 0x538f0 vowel/yy_3
+S 0x539f4 vowel/u#_2
+S 0x53af8 vowel/oe_4
+S 0x53bbc vowel/aa_4
+W 0x53cc0 ufric/sx_sv
+S 0x54508 vowel/y_4
+S 0x5460c vowel/oe_2
+S 0x54710 vwl_no/y#
+S 0x54814 vwl_no/&
+S 0x54918 vwl_no/u#
+S 0x54a1c vwl_no/u#2
+S 0x54b60 vdiph/ai_3
+S 0x54ca4 vwl_no/y#y
+S 0x54de8 vwl_no/au-
+S 0x54fac vowel/y##
+S 0x550b0 vowel/y#_3
+S 0x551b4 vdiph/ou_3
+S 0x552b8 vdiph/y#i_2
+S 0x553fc m/m#_
+S 0x55500 n/n#_
+S 0x55604 n^/n^#_
+S 0x55708 nn/nn#_
+W 0x5580c ufric/tl#
+S 0x56174 r3/r#_
+E 0x561f8 envelope/p_level
+E 0x56278 envelope/p_fall
+E 0x562f8 envelope/p_rise
+E 0x56378 envelope/p_fallrise
+E 0x563f8 envelope/p_214
+E 0x56478 envelope/vi_5amp
+E 0x564f8 envelope/vi_6amp
+S 0x56578 vowel/u#_5
+S 0x5667c vowel/@_2
+S 0x56780 vdiph/&i_2
+S 0x568c4 vdiph/ooi_4
+S 0x56a08 vdiph/u-i
+S 0x56b4c vdiph/aau_4
+S 0x56cd0 vdiph2/ii@_3
+S 0x56e54 vnasal/&u_n
+S 0x56f98 vietnam/oe
+S 0x570dc vietnam/ie_2
+S 0x57220 vowel/u_3
+W 0x57324 vietnam/_c
+S 0x574f0 l/l_vi
+W 0x575f4 vietnam/th
+W 0x57be4 vietnam/tr
+W 0x57ed4 vietnam/dd
+W 0x58478 vietnam/ch
+E 0x589d4 envelope/i_risefall
+S 0x58a54 vietnam/a
+S 0x58b98 vowel/o_5
+S 0x58c9c vwl_zh/ang
+S 0x58ea0 vwl_zh/aang
+S 0x590e4 vdiph/au_2
+S 0x59268 vwl_zh/eng
+S 0x5946c vwl_zh/ing
+S 0x596b0 vwl_zh/ng
+S 0x597f4 vwl_zh/oeng
+S 0x599b8 vwl_zh/ong
+S 0x59b3c vwl_zh/ung
+S 0x59cc0 vowel/8_3
+S 0x59dc4 nn/nn2_
+W 0x59e88 ustop/k_unasp_
+W 0x5a144 ustop/tsh_pzd_unasp
+W 0x5a994 ustop/tsh_pzd
+W 0x5b4d4 ustop/ts_unasp
+W 0x5bd4c ustop/ts_rfx_unasp
+W 0x5c938 ustop/ts_rfx
+S 0x5d618 vwl_zh/a_n
+S 0x5d71c vwl_zh/aau
+S 0x5d860 vowel/i#_6
+S 0x5d964 vwl_zh/iaa
+S 0x5daa8 vwl_zh/iaau
+S 0x5dc2c vwl_zh/ie
+S 0x5dd70 vdiph2/iioo
+S 0x5deb4 vwl_zh/iou
+S 0x5e038 vowel/8
+S 0x5e13c vwl_zh/uaa
+S 0x5e280 vwl_zh/uai
+S 0x5e404 vwl_zh/uei
+S 0x5e588 vwl_zh/uo
+S 0x5e70c vwl_zh/y&
+S 0x5e850 vwl_zh/yee
+S 0x5e994 vdiph2/y@
+S 0x5ead8 vowel/o_4
+W 0x5ebdc ustop/t_hard
+S 0x5ee1c vowel/u#_7
+S 0x5ef20 vowel/8_5
+S 0x5f024 vowel/o_7
+S 0x5f128 vowel/uu#
+S 0x5f22c vowel/8_6
+S 0x5f330 vowel/ee_4
+S 0x5f434 vdiph2/ye
+S 0x5f578 l/l_front_
+S 0x5f6bc l/l_front
+S 0x5f7c0 l/l_4
+S 0x5f884 r/a_
+S 0x5f948 r/aa
+W 0x5fa4c ustop/k_asp_u
+E 0x6027c envelope/i_risefall2
+W 0x602fc ustop/ki_ejc
+W 0x60708 ufric/tlh
+S 0x61c24 vowel/e_6
+S 0x61d28 vowel/&_3
+W 0x61e2c ustop/k_ejc
+S 0x625dc d/dr2
+S 0x626a0 vdiph/@i_4
+S 0x62824 vnasal/ai_n
+S 0x62968 vdiph/a#u
+S 0x62aec vnasal/au_n
+S 0x62c30 vwl_tn/r@
+S 0x62cf4 vwl_tn/@
+S 0x62db8 vwl_tn/I
+S 0x62e7c vdiph2/ii@_2
+S 0x63000 vowel/ii_8
+S 0x63104 vowel/y#_4
+W 0x63208 ustop/t_unasp
diff --git a/navit/support/espeak/espeak-data/phonindex b/navit/support/espeak/espeak-data/phonindex
index 8bed38930..ac5d40fba 100644
--- a/navit/support/espeak/espeak-data/phonindex
+++ b/navit/support/espeak/espeak-data/phonindex
Binary files differ
diff --git a/navit/support/espeak/espeak-data/phontab b/navit/support/espeak/espeak-data/phontab
index c0d3a751d..97fdf16be 100644
--- a/navit/support/espeak/espeak-data/phontab
+++ b/navit/support/espeak/espeak-data/phontab
Binary files differ
diff --git a/navit/support/espeak/espeak-data/pl_dict b/navit/support/espeak/espeak-data/pl_dict
index e46b00b04..192f868bf 100644
--- a/navit/support/espeak/espeak-data/pl_dict
+++ b/navit/support/espeak/espeak-data/pl_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/pt_dict b/navit/support/espeak/espeak-data/pt_dict
index 9ba2a7a78..49d51ceac 100644
--- a/navit/support/espeak/espeak-data/pt_dict
+++ b/navit/support/espeak/espeak-data/pt_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ro_dict b/navit/support/espeak/espeak-data/ro_dict
index b0a72f406..c4d6eacdc 100644
--- a/navit/support/espeak/espeak-data/ro_dict
+++ b/navit/support/espeak/espeak-data/ro_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ru_dict b/navit/support/espeak/espeak-data/ru_dict
index 52d652ccd..edcfe895a 100644
--- a/navit/support/espeak/espeak-data/ru_dict
+++ b/navit/support/espeak/espeak-data/ru_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/si_dict b/navit/support/espeak/espeak-data/si_dict
new file mode 100644
index 000000000..97c7bd12f
--- /dev/null
+++ b/navit/support/espeak/espeak-data/si_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/sk_dict b/navit/support/espeak/espeak-data/sk_dict
index a5d7e7c75..9421432c5 100644
--- a/navit/support/espeak/espeak-data/sk_dict
+++ b/navit/support/espeak/espeak-data/sk_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/sl_dict b/navit/support/espeak/espeak-data/sl_dict
new file mode 100644
index 000000000..a0262a18c
--- /dev/null
+++ b/navit/support/espeak/espeak-data/sl_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/sq_dict b/navit/support/espeak/espeak-data/sq_dict
index aea3d0625..9eb51177f 100644
--- a/navit/support/espeak/espeak-data/sq_dict
+++ b/navit/support/espeak/espeak-data/sq_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/sv_dict b/navit/support/espeak/espeak-data/sv_dict
index 1327d8d0c..ae21dd5d5 100644
--- a/navit/support/espeak/espeak-data/sv_dict
+++ b/navit/support/espeak/espeak-data/sv_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/sw_dict b/navit/support/espeak/espeak-data/sw_dict
index 77e2fd233..0b74a9363 100644
--- a/navit/support/espeak/espeak-data/sw_dict
+++ b/navit/support/espeak/espeak-data/sw_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ta_dict b/navit/support/espeak/espeak-data/ta_dict
index 214e2f4da..033ec9367 100644
--- a/navit/support/espeak/espeak-data/ta_dict
+++ b/navit/support/espeak/espeak-data/ta_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/te_dict b/navit/support/espeak/espeak-data/te_dict
new file mode 100644
index 000000000..cb502f0df
--- /dev/null
+++ b/navit/support/espeak/espeak-data/te_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/tr_dict b/navit/support/espeak/espeak-data/tr_dict
index a3f685047..3bc6fa4ba 100644
--- a/navit/support/espeak/espeak-data/tr_dict
+++ b/navit/support/espeak/espeak-data/tr_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/ur_dict b/navit/support/espeak/espeak-data/ur_dict
new file mode 100644
index 000000000..b74d901f7
--- /dev/null
+++ b/navit/support/espeak/espeak-data/ur_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/vi_dict b/navit/support/espeak/espeak-data/vi_dict
index 24a645a03..c2aee44b6 100644
--- a/navit/support/espeak/espeak-data/vi_dict
+++ b/navit/support/espeak/espeak-data/vi_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/voices/!v/f1 b/navit/support/espeak/espeak-data/voices/!v/f1
index 13664a34f..8f03a73a4 100755
--- a/navit/support/espeak/espeak-data/voices/!v/f1
+++ b/navit/support/espeak/espeak-data/voices/!v/f1
@@ -1,9 +1,9 @@
language variant
name female1
-gender female
+gender female 70
-pitch 145 200
-flutter 7
+pitch 140 200
+flutter 8
roughness 4
formant 0 115 80 150
formant 1 120 80 180
@@ -15,4 +15,4 @@ formant 6 105 80 150
formant 7 110 70 150
formant 8 110 70 150
-stressAdd -10 -10 -20 -20 0 0 40 70
+stressAdd -10 -10 -20 -20 0 0 40 60
diff --git a/navit/support/espeak/espeak-data/voices/!v/f2 b/navit/support/espeak/espeak-data/voices/!v/f2
index e92946707..4122d96b3 100755
--- a/navit/support/espeak/espeak-data/voices/!v/f2
+++ b/navit/support/espeak/espeak-data/voices/!v/f2
@@ -18,3 +18,4 @@ formant 8 110 70 150
stressAdd 0 0 -10 -10 0 0 10 40
breath 0 2 3 3 3 3 3 2
echo 140 10
+consonants 125 125
diff --git a/navit/support/espeak/espeak-data/voices/!v/f3 b/navit/support/espeak/espeak-data/voices/!v/f3
index 92a158281..92a158281 100644..100755
--- a/navit/support/espeak/espeak-data/voices/!v/f3
+++ b/navit/support/espeak/espeak-data/voices/!v/f3
diff --git a/navit/support/espeak/espeak-data/voices/!v/f4 b/navit/support/espeak/espeak-data/voices/!v/f4
index 52c5ac935..52c5ac935 100644..100755
--- a/navit/support/espeak/espeak-data/voices/!v/f4
+++ b/navit/support/espeak/espeak-data/voices/!v/f4
diff --git a/navit/support/espeak/espeak-data/voices/!v/f5 b/navit/support/espeak/espeak-data/voices/!v/f5
new file mode 100755
index 000000000..f43b093ec
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/!v/f5
@@ -0,0 +1,23 @@
+language variant
+name female5
+gender female
+
+pitch 160 228
+roughness 0
+
+formant 0 105 80 150
+formant 1 110 80 160
+formant 2 110 70 150
+formant 3 110 70 150
+formant 4 115 80 200
+formant 5 115 80 100
+formant 6 110 70 150
+formant 7 110 70 100
+formant 8 110 70 150
+
+stressAdd 0 0 -10 -10 0 0 10 40
+breath 0 4 6 6 6 6 0 10
+echo 140 10
+voicing 75
+consonants 150 150
+breathw 150 150 200 200 400 400
diff --git a/navit/support/espeak/espeak-data/voices/!v/fast b/navit/support/espeak/espeak-data/voices/!v/fast
deleted file mode 100755
index 30441d7e2..000000000
--- a/navit/support/espeak/espeak-data/voices/!v/fast
+++ /dev/null
@@ -1,11 +0,0 @@
-language variant
-name fast_test
-
-// Try decreasing these values to make eSpeak's fastest speed faster.
-// The 3 parameters affect:
-// pauses,
-// unvoiced consonants,
-// vowels and voiced consonants
-// The default values are: fast 15 72 110
-
-fast_test 15 72 110
diff --git a/navit/support/espeak/espeak-data/voices/!v/klatt b/navit/support/espeak/espeak-data/voices/!v/klatt
new file mode 100755
index 000000000..b739a86e4
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/!v/klatt
@@ -0,0 +1,4 @@
+language variant
+name klatt
+klatt 1
+
diff --git a/navit/support/espeak/espeak-data/voices/!v/klatt2 b/navit/support/espeak/espeak-data/voices/!v/klatt2
new file mode 100755
index 000000000..01477be54
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/!v/klatt2
@@ -0,0 +1,4 @@
+language variant
+name klatt2
+klatt 2
+
diff --git a/navit/support/espeak/espeak-data/voices/!v/klatt3 b/navit/support/espeak/espeak-data/voices/!v/klatt3
new file mode 100755
index 000000000..b1a874be6
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/!v/klatt3
@@ -0,0 +1,4 @@
+language variant
+name klatt3
+klatt 3
+
diff --git a/navit/support/espeak/espeak-data/voices/!v/klatt4 b/navit/support/espeak/espeak-data/voices/!v/klatt4
new file mode 100644
index 000000000..65278087c
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/!v/klatt4
@@ -0,0 +1,4 @@
+language variant
+name klatt4
+klatt 4
+
diff --git a/navit/support/espeak/espeak-data/voices/!v/m1 b/navit/support/espeak/espeak-data/voices/!v/m1
index 57603a885..4cc9a00bf 100755
--- a/navit/support/espeak/espeak-data/voices/!v/m1
+++ b/navit/support/espeak/espeak-data/voices/!v/m1
@@ -2,18 +2,19 @@ language variant
name male1
gender male 70
-pitch 74 109
-flutter 4
+pitch 75 109
+flutter 5
roughness 4
+consonants 80 100
-formant 0 98 95 100
-formant 1 97 95 100
+formant 0 98 100 100
+formant 1 97 100 100
formant 2 97 95 100
-formant 3 97 100 100
-formant 4 97 100 100
-formant 5 105 100 100
-formant 6 95 100 100
+formant 3 97 95 100
+formant 4 97 85 100
+formant 5 105 80 100
+formant 6 95 80 100
formant 7 100 100 100
formant 8 100 100 100
-stressAdd -10 -10 -20 -20 0 0 40 70
+//stressAdd -10 -10 -20 -20 0 0 40 70
diff --git a/navit/support/espeak/espeak-data/voices/!v/m2 b/navit/support/espeak/espeak-data/voices/!v/m2
index c234f4687..c234f4687 100644..100755
--- a/navit/support/espeak/espeak-data/voices/!v/m2
+++ b/navit/support/espeak/espeak-data/voices/!v/m2
diff --git a/navit/support/espeak/espeak-data/voices/!v/m3 b/navit/support/espeak/espeak-data/voices/!v/m3
index 581cd883f..00479dc35 100644..100755
--- a/navit/support/espeak/espeak-data/voices/!v/m3
+++ b/navit/support/espeak/espeak-data/voices/!v/m3
@@ -13,4 +13,5 @@ formant 6 100 100 100
formant 7 100 100 100
formant 8 100 100 100
+consonants 100
stressAdd 10 10 0 0 0 0 -30 -30
diff --git a/navit/support/espeak/espeak-data/voices/!v/m4 b/navit/support/espeak/espeak-data/voices/!v/m4
index 7199341c3..7199341c3 100644..100755
--- a/navit/support/espeak/espeak-data/voices/!v/m4
+++ b/navit/support/espeak/espeak-data/voices/!v/m4
diff --git a/navit/support/espeak/espeak-data/voices/!v/m7 b/navit/support/espeak/espeak-data/voices/!v/m7
index 05f5b3e86..11b49ed2a 100755
--- a/navit/support/espeak/espeak-data/voices/!v/m7
+++ b/navit/support/espeak/espeak-data/voices/!v/m7
@@ -1,18 +1,17 @@
language variant
-gender male 35
-name male5
+name male7
+gender male
-formant 0 120 150 220
+pitch 75 125
-formant 1 100 100 100
-formant 2 100 100 100
-formant 3 100 100 100
+formant 0 100 125 100
+formant 1 100 90 80
+formant 2 100 70 90
+formant 3 100 60 90
+formant 4 100 60 90
+formant 5 75 50 90
+formant 6 90 50 100
+formant 7 100 50 100
+formant 8 100 50 100
+voicing 155
-formant 4 100 80 100
-formant 5 100 100 100
-
-formant 6 80 200 80
-formant 7 80 200 100
-
-pitch 80 118
-tone 600 150 255 50 100 100
diff --git a/navit/support/espeak/espeak-data/voices/!v/whisperf b/navit/support/espeak/espeak-data/voices/!v/whisperf
new file mode 100755
index 000000000..f239e8a29
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/!v/whisperf
@@ -0,0 +1,24 @@
+language variant
+name female_whisper
+gender female
+
+pitch 160 220
+roughness 3
+
+formant 0 105 0 150
+formant 1 110 40 160
+formant 2 110 70 150
+formant 3 110 70 150
+formant 4 115 80 150
+formant 5 115 80 150
+formant 6 110 70 150
+formant 7 110 70 150
+formant 8 110 70 150
+
+stressAdd 0 0 -10 -10 0 0 10 40
+
+// whisper
+voicing 20
+breath 75 75 50 40 15 10
+breathw 150 150 200 200 400 400
+
diff --git a/navit/support/espeak/espeak-data/voices/asia/fa b/navit/support/espeak/espeak-data/voices/asia/fa
new file mode 100755
index 000000000..60ee250e8
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/fa
@@ -0,0 +1,3 @@
+name persian
+language fa
+
diff --git a/navit/support/espeak/espeak-data/voices/asia/fa-pin b/navit/support/espeak/espeak-data/voices/asia/fa-pin
new file mode 100755
index 000000000..f3adbfca4
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/fa-pin
@@ -0,0 +1,6 @@
+name persian-pinglish
+// Sometimes, Farsi speakers write Farsi words using English characters, particularly in Chat and SMS (texte messages).), called Pinglish
+language fa-pin
+dictrules 1
+phonemes fa
+
diff --git a/navit/support/espeak/espeak-data/voices/asia/hi b/navit/support/espeak/espeak-data/voices/asia/hi
new file mode 100755
index 000000000..2d41b07b3
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/hi
@@ -0,0 +1,3 @@
+name hindi
+language hi
+gender male
diff --git a/navit/support/espeak/espeak-data/voices/hy b/navit/support/espeak/espeak-data/voices/asia/hy
index 6c65e3c68..6c65e3c68 100644..100755
--- a/navit/support/espeak/espeak-data/voices/hy
+++ b/navit/support/espeak/espeak-data/voices/asia/hy
diff --git a/navit/support/espeak/espeak-data/voices/hy-west b/navit/support/espeak/espeak-data/voices/asia/hy-west
index 46317618d..85b3f75b6 100644..100755
--- a/navit/support/espeak/espeak-data/voices/hy-west
+++ b/navit/support/espeak/espeak-data/voices/asia/hy-west
@@ -1,19 +1,22 @@
name armenian-west
-language hy
+language hy-west
+language hy 8
gender male
+dictrules 1
+
// change consonants for West Armenian pronunciation
replace 00 b p#
replace 00 d t#
replace 00 dz ts#
-replace 00 dZ tS
+replace 00 dZ tS#
replace 00 g k#
replace 00 p b
replace 00 t d
replace 00 ts dz
-replace 00 c dZ
+replace 00 tS dZ
replace 00 k g
-replace 00 ** R // ??
-replace 00 r R
+replace 00 R2 R // ??
+
diff --git a/navit/support/espeak/espeak-data/voices/id b/navit/support/espeak/espeak-data/voices/asia/id
index ce800f70b..f35b116af 100755
--- a/navit/support/espeak/espeak-data/voices/id
+++ b/navit/support/espeak/espeak-data/voices/asia/id
@@ -1,4 +1,4 @@
-name indonesian-test
+name indonesian
language id
gender male
diff --git a/navit/support/espeak/espeak-data/voices/asia/ka b/navit/support/espeak/espeak-data/voices/asia/ka
new file mode 100755
index 000000000..6f748b7f6
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/ka
@@ -0,0 +1,2 @@
+name georgian
+language ka
diff --git a/navit/support/espeak/espeak-data/voices/asia/kn b/navit/support/espeak/espeak-data/voices/asia/kn
new file mode 100755
index 000000000..ced1b1fa9
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/kn
@@ -0,0 +1,5 @@
+name kannada
+language kn
+
+intonation 2
+//consonants 80
diff --git a/navit/support/espeak/espeak-data/voices/ku b/navit/support/espeak/espeak-data/voices/asia/ku
index 536957cb8..536957cb8 100755
--- a/navit/support/espeak/espeak-data/voices/ku
+++ b/navit/support/espeak/espeak-data/voices/asia/ku
diff --git a/navit/support/espeak/espeak-data/voices/asia/ml b/navit/support/espeak/espeak-data/voices/asia/ml
new file mode 100755
index 000000000..45bdf8528
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/ml
@@ -0,0 +1,6 @@
+name malayalam
+language ml
+gender male
+
+intonation 2
+//consonants 80
diff --git a/navit/support/espeak/espeak-data/voices/asia/ms b/navit/support/espeak/espeak-data/voices/asia/ms
new file mode 100755
index 000000000..90a38a37c
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/ms
@@ -0,0 +1,17 @@
+// Last updated: 14 October 2010, Jason Ong (jason@portalgroove.com)
+name malay
+language ms
+gender male
+phonemes id
+
+translator id
+
+stressLength 160 200 180 180 0 0 220 240
+stressAmp 16 18 18 18 0 0 22 21
+intonation 3 // Less intonation, and comma does not raise the pitch.
+
+// Nuance - Peninsula Malaysia
+// replace 3 a @ // change 'saya' to 'saye'
+ // (only the last phoneme of a word, only in unstressed syllables)
+
+consonants 80 80
diff --git a/navit/support/espeak/espeak-data/voices/asia/ne b/navit/support/espeak/espeak-data/voices/asia/ne
new file mode 100755
index 000000000..fc8295329
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/ne
@@ -0,0 +1,5 @@
+name nepali
+language ne
+gender male
+
+dictrules 1
diff --git a/navit/support/espeak/espeak-data/voices/asia/pa b/navit/support/espeak/espeak-data/voices/asia/pa
new file mode 100755
index 000000000..4cd585d1d
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/pa
@@ -0,0 +1,2 @@
+name punjabi
+language pa
diff --git a/navit/support/espeak/espeak-data/voices/ta b/navit/support/espeak/espeak-data/voices/asia/ta
index 8848d6820..8848d6820 100755
--- a/navit/support/espeak/espeak-data/voices/ta
+++ b/navit/support/espeak/espeak-data/voices/asia/ta
diff --git a/navit/support/espeak/espeak-data/voices/tr b/navit/support/espeak/espeak-data/voices/asia/tr
index 4f1904e51..4f1904e51 100755
--- a/navit/support/espeak/espeak-data/voices/tr
+++ b/navit/support/espeak/espeak-data/voices/asia/tr
diff --git a/navit/support/espeak/espeak-data/voices/vi b/navit/support/espeak/espeak-data/voices/asia/vi
index 1596e3c77..dbd23b566 100755
--- a/navit/support/espeak/espeak-data/voices/vi
+++ b/navit/support/espeak/espeak-data/voices/asia/vi
@@ -1,4 +1,4 @@
-name vietnam-test
+name vietnam
language vi
gender male
diff --git a/navit/support/espeak/espeak-data/voices/asia/vi-hue b/navit/support/espeak/espeak-data/voices/asia/vi-hue
new file mode 100755
index 000000000..83849f897
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/vi-hue
@@ -0,0 +1,12 @@
+name vietnam_hue
+language vi-hue
+phonemes vi-hue
+dictrules 1
+gender male
+
+words 1
+pitch 82 118 //80 118
+//breath 75 75 60 40 15 10
+ //breathw 150 150 200 200 400 400
+ voicing 90 //18
+ flutter 20
diff --git a/navit/support/espeak/espeak-data/voices/asia/vi-sgn b/navit/support/espeak/espeak-data/voices/asia/vi-sgn
new file mode 100755
index 000000000..07c4a12e2
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/asia/vi-sgn
@@ -0,0 +1,12 @@
+name vietnam_sgn
+language vi-sgn
+phonemes vi-sgn
+dictrules 2
+gender male
+
+words 1
+pitch 82 118 //80 118
+//breath 75 75 60 40 15 10
+ //breathw 150 150 200 200 400 400
+ voicing 90 //18
+ flutter 20
diff --git a/navit/support/espeak/espeak-data/voices/zh b/navit/support/espeak/espeak-data/voices/asia/zh
index 03edde41d..559a64030 100644..100755
--- a/navit/support/espeak/espeak-data/voices/zh
+++ b/navit/support/espeak/espeak-data/voices/asia/zh
@@ -4,6 +4,8 @@ gender male
words 1
pitch 80 118
+dict_min 100000
+
//for some dialects
//[en]: replace ng with n
diff --git a/navit/support/espeak/espeak-data/voices/zh-yue b/navit/support/espeak/espeak-data/voices/asia/zh-yue
index ba8723264..8e2b8a7ed 100755
--- a/navit/support/espeak/espeak-data/voices/zh-yue
+++ b/navit/support/espeak/espeak-data/voices/asia/zh-yue
@@ -1,4 +1,4 @@
-name cantonese-test
+name cantonese
language zh-yue
language yue
language zhy
@@ -12,3 +12,4 @@ gender male
dictrules 1
words 1
+dict_min 10000
diff --git a/navit/support/espeak/espeak-data/voices/da b/navit/support/espeak/espeak-data/voices/da
deleted file mode 100755
index ec9936cc2..000000000
--- a/navit/support/espeak/espeak-data/voices/da
+++ /dev/null
@@ -1,3 +0,0 @@
-name danish-test
-language da
-gender male
diff --git a/navit/support/espeak/espeak-data/voices/en/en b/navit/support/espeak/espeak-data/voices/en
index 43e2ca1fc..84d12dd8c 100755
--- a/navit/support/espeak/espeak-data/voices/en/en
+++ b/navit/support/espeak/espeak-data/voices/en
@@ -1,9 +1,9 @@
name english
+language en-gb 2
language en-uk 2
language en 2
gender male
-//pitch 80 117
+//pitch 80 118
-replace 03 I i
-replace 03 I2 i
+tunes s1 c1 q1 e1
diff --git a/navit/support/espeak/espeak-data/voices/en/en-us b/navit/support/espeak/espeak-data/voices/en-us
index c2656b28c..6846aa8b7 100755
--- a/navit/support/espeak/espeak-data/voices/en/en-us
+++ b/navit/support/espeak/espeak-data/voices/en-us
@@ -5,13 +5,13 @@ language en-r
language en 3
gender male
-phonemes en_us
+phonemes en-us
dictrules 3 6
-option 12 1 // reduce [t]
+option reduce_t 1
-stressLength 145 125 170 170 0 0 265 290
-stressAmp 17 16 20 20 20 22 22 20
+stressLength 140 120 190 170 0 0 255 300
+stressAmp 17 16 19 19 19 19 21 19
replace 03 I i
replace 03 I2 i
-replace 03 @ @/
+
diff --git a/navit/support/espeak/espeak-data/voices/es-la b/navit/support/espeak/espeak-data/voices/es-la
index c326c46f5..4fbacd1bc 100755
--- a/navit/support/espeak/espeak-data/voices/es-la
+++ b/navit/support/espeak/espeak-data/voices/es-la
@@ -1,11 +1,12 @@
-name spanish-latin-american
+name spanish-latin-am
language es-la
language es-mx 6
+language es 6
gender male
-phonemes es_la
+phonemes es-la
dictrules 2
intonation 2
-stressLength 170 200 180 180 0 0 250 280
+stressLength 170 200 230 180 0 0 250 280
replace 00 T s
diff --git a/navit/support/espeak/espeak-data/voices/europe/an b/navit/support/espeak/espeak-data/voices/europe/an
new file mode 100755
index 000000000..7bf245e03
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/europe/an
@@ -0,0 +1,3 @@
+name aragonese
+language an
+gender male
diff --git a/navit/support/espeak/espeak-data/voices/europe/bg b/navit/support/espeak/espeak-data/voices/europe/bg
new file mode 100755
index 000000000..c3842ca78
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/europe/bg
@@ -0,0 +1,5 @@
+name bulgarian
+language bg
+
+stressAmp 13 12 17 17 20 22 22 21
+stressLength 180 170 200 200 200 200 210 220 \ No newline at end of file
diff --git a/navit/support/espeak/espeak-data/voices/bs b/navit/support/espeak/espeak-data/voices/europe/bs
index eadd70732..eadd70732 100755
--- a/navit/support/espeak/espeak-data/voices/bs
+++ b/navit/support/espeak/espeak-data/voices/europe/bs
diff --git a/navit/support/espeak/espeak-data/voices/ca b/navit/support/espeak/espeak-data/voices/europe/ca
index dc51396ce..dc51396ce 100644..100755
--- a/navit/support/espeak/espeak-data/voices/ca
+++ b/navit/support/espeak/espeak-data/voices/europe/ca
diff --git a/navit/support/espeak/espeak-data/voices/cs b/navit/support/espeak/espeak-data/voices/europe/cs
index 1c2992dd5..1c2992dd5 100755
--- a/navit/support/espeak/espeak-data/voices/cs
+++ b/navit/support/espeak/espeak-data/voices/europe/cs
diff --git a/navit/support/espeak/espeak-data/voices/cy b/navit/support/espeak/espeak-data/voices/europe/cy
index 2991e99d1..30abd9432 100755
--- a/navit/support/espeak/espeak-data/voices/cy
+++ b/navit/support/espeak/espeak-data/voices/europe/cy
@@ -1,5 +1,5 @@
language cy
-name welsh-test
+name welsh
gender male
intonation 4
diff --git a/navit/support/espeak/espeak-data/voices/europe/da b/navit/support/espeak/espeak-data/voices/europe/da
new file mode 100755
index 000000000..12ce1096a
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/europe/da
@@ -0,0 +1,7 @@
+name danish
+language da
+gender male
+
+tunes s2 c2 q2 e2
+
+
diff --git a/navit/support/espeak/espeak-data/voices/el b/navit/support/espeak/espeak-data/voices/europe/el
index 1e9a757f8..1e9a757f8 100755
--- a/navit/support/espeak/espeak-data/voices/el
+++ b/navit/support/espeak/espeak-data/voices/europe/el
diff --git a/navit/support/espeak/espeak-data/voices/es b/navit/support/espeak/espeak-data/voices/europe/es
index 1a9e53b6e..43c852c8b 100755
--- a/navit/support/espeak/espeak-data/voices/es
+++ b/navit/support/espeak/espeak-data/voices/europe/es
@@ -3,5 +3,5 @@ language es
gender male
dictrules 1
-intonation 3
+//intonation 3
diff --git a/navit/support/espeak/espeak-data/voices/europe/et b/navit/support/espeak/espeak-data/voices/europe/et
new file mode 100755
index 000000000..6caf78ce0
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/europe/et
@@ -0,0 +1,3 @@
+name estonian
+language et
+
diff --git a/navit/support/espeak/espeak-data/voices/fi b/navit/support/espeak/espeak-data/voices/europe/fi
index 6e11c9312..6e11c9312 100755
--- a/navit/support/espeak/espeak-data/voices/fi
+++ b/navit/support/espeak/espeak-data/voices/europe/fi
diff --git a/navit/support/espeak/espeak-data/voices/europe/fr-be b/navit/support/espeak/espeak-data/voices/europe/fr-be
new file mode 100755
index 000000000..4a7418204
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/europe/fr-be
@@ -0,0 +1,9 @@
+name french-Belgium
+language fr-be
+language fr 8
+gender male
+
+dictrules 2
+tunes s3 c3 q3 e3
+
+
diff --git a/navit/support/espeak/espeak-data/voices/europe/ga b/navit/support/espeak/espeak-data/voices/europe/ga
new file mode 100755
index 000000000..30d32e07e
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/europe/ga
@@ -0,0 +1,4 @@
+name irish-gaeilge
+language ga
+
+dictrules 1 // fix for eclipsis
diff --git a/navit/support/espeak/espeak-data/voices/hr b/navit/support/espeak/espeak-data/voices/europe/hr
index d6811d3ae..d6811d3ae 100755
--- a/navit/support/espeak/espeak-data/voices/hr
+++ b/navit/support/espeak/espeak-data/voices/europe/hr
diff --git a/navit/support/espeak/espeak-data/voices/hu b/navit/support/espeak/espeak-data/voices/europe/hu
index ba2bdde41..25d78c05c 100755
--- a/navit/support/espeak/espeak-data/voices/hu
+++ b/navit/support/espeak/espeak-data/voices/europe/hu
@@ -1,3 +1,7 @@
name hungarian
language hu
gender male
+option bracket 0 0
+pitch 81 117
+
+
diff --git a/navit/support/espeak/espeak-data/voices/is b/navit/support/espeak/espeak-data/voices/europe/is
index 9e9c4e747..b8f238345 100755
--- a/navit/support/espeak/espeak-data/voices/is
+++ b/navit/support/espeak/espeak-data/voices/europe/is
@@ -1,4 +1,4 @@
-name icelandic-test
+name icelandic
language is
gender male
diff --git a/navit/support/espeak/espeak-data/voices/it b/navit/support/espeak/espeak-data/voices/europe/it
index 53c2a7048..cf4966c8d 100755
--- a/navit/support/espeak/espeak-data/voices/it
+++ b/navit/support/espeak/espeak-data/voices/europe/it
@@ -1,6 +1,8 @@
+
name italian
language it
gender male
-replace 03 i I
+tunes s4 c4 q4 e4
+replace 03 i I
diff --git a/navit/support/espeak/espeak-data/voices/europe/lt b/navit/support/espeak/espeak-data/voices/europe/lt
new file mode 100755
index 000000000..8295f6b26
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/europe/lt
@@ -0,0 +1,5 @@
+
+name lithuanian
+language lt
+gender male
+
diff --git a/navit/support/espeak/espeak-data/voices/lv b/navit/support/espeak/espeak-data/voices/europe/lv
index 0278ea213..0278ea213 100755
--- a/navit/support/espeak/espeak-data/voices/lv
+++ b/navit/support/espeak/espeak-data/voices/europe/lv
diff --git a/navit/support/espeak/espeak-data/voices/mk b/navit/support/espeak/espeak-data/voices/europe/mk
index 4607dd079..47a8398c9 100755
--- a/navit/support/espeak/espeak-data/voices/mk
+++ b/navit/support/espeak/espeak-data/voices/europe/mk
@@ -1,4 +1,4 @@
-name macedonian-test
+name macedonian
language mk
gender male
diff --git a/navit/support/espeak/espeak-data/voices/nl b/navit/support/espeak/espeak-data/voices/europe/nl
index 6a8d5efd8..beef33fa3 100755
--- a/navit/support/espeak/espeak-data/voices/nl
+++ b/navit/support/espeak/espeak-data/voices/europe/nl
@@ -1,3 +1,3 @@
language nl
-name dutch-test
+name dutch
gender male
diff --git a/navit/support/espeak/espeak-data/voices/no b/navit/support/espeak/espeak-data/voices/europe/no
index 77b60b963..a90c7b725 100755
--- a/navit/support/espeak/espeak-data/voices/no
+++ b/navit/support/espeak/espeak-data/voices/europe/no
@@ -1,4 +1,4 @@
-name norwegian-test
+name norwegian
language no
language nb
gender male
diff --git a/navit/support/espeak/espeak-data/voices/pl b/navit/support/espeak/espeak-data/voices/europe/pl
index 8fc65d4ba..8fc65d4ba 100755
--- a/navit/support/espeak/espeak-data/voices/pl
+++ b/navit/support/espeak/espeak-data/voices/europe/pl
diff --git a/navit/support/espeak/espeak-data/voices/pt-pt b/navit/support/espeak/espeak-data/voices/europe/pt-pt
index e23915f39..43cb971c4 100755
--- a/navit/support/espeak/espeak-data/voices/pt-pt
+++ b/navit/support/espeak/espeak-data/voices/europe/pt-pt
@@ -1,7 +1,8 @@
name portugal
language pt-pt
+language pt 6
gender male
-phonemes pt_pt
+phonemes pt-pt
dictrules 1
intonation 2
diff --git a/navit/support/espeak/espeak-data/voices/ro b/navit/support/espeak/espeak-data/voices/europe/ro
index d8ecd252c..d8ecd252c 100755
--- a/navit/support/espeak/espeak-data/voices/ro
+++ b/navit/support/espeak/espeak-data/voices/europe/ro
diff --git a/navit/support/espeak/espeak-data/voices/ru b/navit/support/espeak/espeak-data/voices/europe/ru
index 238c69126..4136ab21c 100755
--- a/navit/support/espeak/espeak-data/voices/ru
+++ b/navit/support/espeak/espeak-data/voices/europe/ru
@@ -1,6 +1,7 @@
-name russian_test
+name russian
language ru
gender male
replace 03 a a#
+dict_min 20000
diff --git a/navit/support/espeak/espeak-data/voices/sk b/navit/support/espeak/espeak-data/voices/europe/sk
index 026363f6a..026363f6a 100755
--- a/navit/support/espeak/espeak-data/voices/sk
+++ b/navit/support/espeak/espeak-data/voices/europe/sk
diff --git a/navit/support/espeak/espeak-data/voices/sq b/navit/support/espeak/espeak-data/voices/europe/sq
index d0b729579..d0b729579 100644..100755
--- a/navit/support/espeak/espeak-data/voices/sq
+++ b/navit/support/espeak/espeak-data/voices/europe/sq
diff --git a/navit/support/espeak/espeak-data/voices/sr b/navit/support/espeak/espeak-data/voices/europe/sr
index a7a8223db..a7a8223db 100644..100755
--- a/navit/support/espeak/espeak-data/voices/sr
+++ b/navit/support/espeak/espeak-data/voices/europe/sr
diff --git a/navit/support/espeak/espeak-data/voices/sv b/navit/support/espeak/espeak-data/voices/europe/sv
index df70f4387..df70f4387 100755
--- a/navit/support/espeak/espeak-data/voices/sv
+++ b/navit/support/espeak/espeak-data/voices/europe/sv
diff --git a/navit/support/espeak/espeak-data/voices/fr b/navit/support/espeak/espeak-data/voices/fr
index 973073161..a0b4986ec 100755
--- a/navit/support/espeak/espeak-data/voices/fr
+++ b/navit/support/espeak/espeak-data/voices/fr
@@ -1,7 +1,7 @@
-language fr
name french
+language fr-fr
+language fr
gender male
dictrules 1
-intonation 3
-
+tunes s3 c3 q3 e3
diff --git a/navit/support/espeak/espeak-data/voices/fr-be b/navit/support/espeak/espeak-data/voices/fr-be
deleted file mode 100755
index cba9b2757..000000000
--- a/navit/support/espeak/espeak-data/voices/fr-be
+++ /dev/null
@@ -1,7 +0,0 @@
-language fr-be
-name french (Belgium)
-gender male
-
-dictrules 2
-intonation 3
-
diff --git a/navit/support/espeak/espeak-data/voices/hi b/navit/support/espeak/espeak-data/voices/hi
deleted file mode 100755
index de4786c94..000000000
--- a/navit/support/espeak/espeak-data/voices/hi
+++ /dev/null
@@ -1,9 +0,0 @@
-name hindi-test
-language hi
-gender male
-
-translator hi
-phonemes hi
-dictionary hi
-
-dictrules 1
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-br1 b/navit/support/espeak/espeak-data/voices/mb/mb-br1
index ba7c42cc4..370a5ea13 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-br1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-br1
@@ -1,7 +1,9 @@
language pt 7
name brazil-mbrola-1
gender male
+
pitch 82 117
+voicing 80
dictrules 2 3 4
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-br3 b/navit/support/espeak/espeak-data/voices/mb/mb-br3
index 8479e658e..0cf9daa53 100644..100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-br3
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-br3
@@ -1,9 +1,10 @@
language pt 7
name brazil-mbrola-3
gender male
-pitch 82 117
+pitch 80 120
dictrules 2 3 4
+voicing 120
mbrola br3 ptbr_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-br4 b/navit/support/espeak/espeak-data/voices/mb/mb-br4
index d3d772007..1cc596d85 100644..100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-br4
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-br4
@@ -1,7 +1,9 @@
language pt 7
name brazil-mbrola-4
gender female
+
pitch 140 220
+voicing 80
dictrules 2 3 4
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-cr1 b/navit/support/espeak/espeak-data/voices/mb/mb-cr1
index 9b280bf8b..1f0ab08a0 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-cr1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-cr1
@@ -5,5 +5,6 @@ gender male
dictionary hbs
dictrules 1
+voicing 150
pitch 82 117
mbrola cr1 cr1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-de2 b/navit/support/espeak/espeak-data/voices/mb/mb-de2
index c0a5475ec..39e3c4343 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-de2
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-de2
@@ -1,6 +1,7 @@
name german-mbrola-2
language de 6
gender male
+voicing 80
mbrola de2 de2_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-de3 b/navit/support/espeak/espeak-data/voices/mb/mb-de3
new file mode 100755
index 000000000..d2ff8dd71
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-de3
@@ -0,0 +1,8 @@
+name german-mbrola-3
+language de 6
+gender female
+voicing 80
+pitch 140 220
+
+mbrola de3 de2_phtrans
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-de4 b/navit/support/espeak/espeak-data/voices/mb/mb-de4
index 31bd479a3..abd8713da 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-de4
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-de4
@@ -2,5 +2,7 @@ name german-mbrola-4
language de 6
gender male
+voicing 130
+
mbrola de4 de4_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-de4-en b/navit/support/espeak/espeak-data/voices/mb/mb-de4-en
index 8fd4a63a1..5980fbf06 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-de4-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-de4-en
@@ -2,5 +2,7 @@ name en-german
language en 9
gender male
+voicing 130
+
mbrola de4 de4_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-de5-en b/navit/support/espeak/espeak-data/voices/mb/mb-de5-en
index e416c6dc5..e30a0257b 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-de5-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-de5-en
@@ -1,5 +1,5 @@
name en-german-5
-language en
+language en 9
gender female
pitch 140 220
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-de6 b/navit/support/espeak/espeak-data/voices/mb/mb-de6
index 35a4a3fc5..35a4a3fc5 100644..100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-de6
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-de6
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-de6-grc b/navit/support/espeak/espeak-data/voices/mb/mb-de6-grc
index a6e0f46bb..a6e0f46bb 100644..100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-de6-grc
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-de6-grc
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-de7 b/navit/support/espeak/espeak-data/voices/mb/mb-de7
index aa80edaad..d51583c86 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-de7
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-de7
@@ -2,6 +2,7 @@ name german-mbrola-7
language de 7
gender female
+voicing 150
pitch 140 220
mbrola de7 de6_phtrans 22050
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-ee1 b/navit/support/espeak/espeak-data/voices/mb/mb-ee1
new file mode 100755
index 000000000..4d62a5b17
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-ee1
@@ -0,0 +1,8 @@
+name estonian-mbrola
+language et
+gender male
+
+pitch 75 125
+voicing 80
+mbrola ee1 ee1_phtrans
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-en1 b/navit/support/espeak/espeak-data/voices/mb/mb-en1
index fc60f4167..8de8e01ca 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-en1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-en1
@@ -3,5 +3,6 @@ language en-uk 3
language en 2
gender male
+voicing 150
pitch 82 117
mbrola en1 en1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-es1 b/navit/support/espeak/espeak-data/voices/mb/mb-es1
index d59fe7952..d5ffea2d1 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-es1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-es1
@@ -4,4 +4,5 @@ gender male
pitch 82 117
mbrola es1 es_phtrans
+voicing 120
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-es2 b/navit/support/espeak/espeak-data/voices/mb/mb-es2
index 42de58882..d38fa08d8 100644..100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-es2
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-es2
@@ -3,5 +3,5 @@ name spanish-mbrola-2
gender male
pitch 82 117
-mbrola es2 es_phtrans
+mbrola es2 es_phtrans 22050
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-fr1 b/navit/support/espeak/espeak-data/voices/mb/mb-fr1
index 7cbdab338..046515271 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-fr1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-fr1
@@ -5,5 +5,7 @@ gender male
dictrules 1
stressLength 180 180 180 180 0 0 220 220
pitch 82 117
+voicing 70
+
mbrola fr1 fr1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-fr1-en b/navit/support/espeak/espeak-data/voices/mb/mb-fr1-en
index 366653147..47cb1825b 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-fr1-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-fr1-en
@@ -4,5 +4,6 @@ gender male
dictrules 1
pitch 82 117
+voicing 70
mbrola fr1 fr1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-fr4 b/navit/support/espeak/espeak-data/voices/mb/mb-fr4
index c276bec0d..8e3c3921a 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-fr4
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-fr4
@@ -4,5 +4,6 @@ gender female
dictrules 1
pitch 140 220
+voicing 90
mbrola fr1 fr1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-fr4-en b/navit/support/espeak/espeak-data/voices/mb/mb-fr4-en
index b8f782946..8126770b4 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-fr4-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-fr4-en
@@ -4,5 +4,6 @@ gender female
dictrules 1
pitch 140 220
+voicing 90
mbrola fr1 fr1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-gr2 b/navit/support/espeak/espeak-data/voices/mb/mb-gr2
index 30dea8920..92d2f20fc 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-gr2
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-gr2
@@ -3,4 +3,5 @@ language el 7
gender male
pitch 82 117
+voicing 65
mbrola gr2 gr2_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-gr2-en b/navit/support/espeak/espeak-data/voices/mb/mb-gr2-en
index b48b1788b..c9bd20d1e 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-gr2-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-gr2-en
@@ -1,6 +1,7 @@
name en-greek
-language en 7
+language en 9
gender male
pitch 82 117
+voicing 65
mbrola gr2 gr2_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-hu1 b/navit/support/espeak/espeak-data/voices/mb/mb-hu1
index b8519559d..195ea330a 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-hu1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-hu1
@@ -3,4 +3,5 @@ language hu 7
gender female
pitch 140 220
+voicing 160
mbrola hu1 hu1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-hu1-en b/navit/support/espeak/espeak-data/voices/mb/mb-hu1-en
index 73ac62a4a..28ce3754d 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-hu1-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-hu1-en
@@ -3,4 +3,5 @@ language en 10
gender female
pitch 140 220
+voicing 160
mbrola hu1 hu1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-ic1 b/navit/support/espeak/espeak-data/voices/mb/mb-ic1
new file mode 100755
index 000000000..b08b53a23
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-ic1
@@ -0,0 +1,7 @@
+name mbrola-icelandic
+language is 6
+gender male
+
+voicing 180
+mbrola ic1 ic1_phtrans
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-id1 b/navit/support/espeak/espeak-data/voices/mb/mb-id1
index b86f59306..aa57c3159 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-id1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-id1
@@ -3,5 +3,6 @@ language id 7
gender male
pitch 82 117
+voicing 120
mbrola id1 id1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-ir1 b/navit/support/espeak/espeak-data/voices/mb/mb-ir1
new file mode 100755
index 000000000..c47d2079b
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-ir1
@@ -0,0 +1,22 @@
+name persian-mb-ir1
+language fa
+gender male
+phonemes fa
+mbrola ir1 ir1_phtrans
+
+// "speed 82" adjust default speed of "mb-ir1" with eSpeak "fa".
+speed 82
+// Please don't change this value. It's result of several tests.
+
+// "voicing 125" adjust output volume of "mb-ir1" with eSpeak "fa".
+voicing 125
+// Please don't change this value. It's result of several tests.
+
+// "pitch 82 118" adjust default pitch of "mb-ir1" like other male voices.
+pitch 82 118
+// Please don't change this value. The result male voice is good and natural.
+
+// If you want use Pinglish instead of English for reading, just delete // from start of next line.
+// dictrules 1
+// "dictrules 1" read English text as Pinglish (Persian text that written with English alphabets).
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-ir2 b/navit/support/espeak/espeak-data/voices/mb/mb-ir2
new file mode 100755
index 000000000..819bfa566
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-ir2
@@ -0,0 +1,22 @@
+name persian-mb-ir2
+language fa
+gender female
+phonemes fa
+mbrola ir2 ir1_phtrans 22050
+
+// "speed 78" adjust default speed of "mb-ir2" with eSpeak "fa".
+speed 78
+// Please don't change this value. It's result of several tests.
+
+// "voicing 150" adjust output volume of "mb-ir2" with eSpeak "fa".
+voicing 150
+// Please don't change this value. It's result of several tests.
+
+// "pitch 140 220" adjust default pitch of "mb-ir2" like other female voices.
+pitch 140 220
+// Please don't change this value. The result female voice is good and natural.
+
+// If you want use Pinglish instead of English for reading, just delete // from start of next line.
+// dictrules 1
+// "dictrules 1" read English text as Pinglish (Persian text that written with English alphabets).
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-it3 b/navit/support/espeak/espeak-data/voices/mb/mb-it3
index 00e88867c..89f7dcd4f 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-it3
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-it3
@@ -3,6 +3,7 @@ language it 7
gender male
pitch 82 117
+voicing 65
mbrola it3 it3_phtrans
replace 03 i I // final unstressed "i"
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-it4 b/navit/support/espeak/espeak-data/voices/mb/mb-it4
index f2130ba4f..cb3a6ffbe 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-it4
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-it4
@@ -3,6 +3,7 @@ language it 7
gender female
pitch 140 220
+voicing 60
mbrola it4 it3_phtrans
replace 03 i I // final unstressed "i"
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-mx1 b/navit/support/espeak/espeak-data/voices/mb/mb-mx1
new file mode 100755
index 000000000..99b723c31
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-mx1
@@ -0,0 +1,10 @@
+language es-mx 7
+language es 8
+name mexican-mbrola-1
+gender male
+pitch 82 117
+
+mbrola mx1 mx1_phtrans
+
+replace 00 T s
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-mx2 b/navit/support/espeak/espeak-data/voices/mb/mb-mx2
new file mode 100755
index 000000000..f46f0be1f
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-mx2
@@ -0,0 +1,10 @@
+language es-mx 7
+language es 8
+name mexican-mbrola-2
+gender male
+pitch 82 117
+
+mbrola mx2 mx2_phtrans
+
+replace 00 T s
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-nl2 b/navit/support/espeak/espeak-data/voices/mb/mb-nl2
index fc377156b..273f98b61 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-nl2
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-nl2
@@ -3,5 +3,7 @@ name dutch-mbrola-2
gender male
pitch 82 117
+voicing 130
+
mbrola nl2 nl_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-nl2-en b/navit/support/espeak/espeak-data/voices/mb/mb-nl2-en
index 0c2d13a65..ad0ac4063 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-nl2-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-nl2-en
@@ -3,5 +3,7 @@ name en-dutch
gender male
pitch 82 117
+voicing 130
+
mbrola nl2 nl_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-pl1 b/navit/support/espeak/espeak-data/voices/mb/mb-pl1
index 4e2b9d23e..cedeb46bb 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-pl1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-pl1
@@ -3,4 +3,5 @@ language pl 7
gender female
pitch 140 220
+voicing 120
mbrola pl1 pl1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-pt1 b/navit/support/espeak/espeak-data/voices/mb/mb-pt1
index ebd92ffb3..a2168959b 100644..100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-pt1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-pt1
@@ -1,9 +1,10 @@
language pt 7
name portugal-mbrola-1
gender female
-pitch 140 220
+pitch 145 240
dictrules 1
+voicing 70
mbrola pt1 pt1_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-sw1 b/navit/support/espeak/espeak-data/voices/mb/mb-sw1
index 4c6239268..57ee32a92 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-sw1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-sw1
@@ -3,5 +3,7 @@ language sv 7
gender male
pitch 82 117
+voicing 120
+
mbrola sw1 sv_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-sw1-en b/navit/support/espeak/espeak-data/voices/mb/mb-sw1-en
index 52692c385..627af2fbb 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-sw1-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-sw1-en
@@ -3,5 +3,7 @@ language en 11
gender male
pitch 82 117
+voicing 120
+
mbrola sw1 sv_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-sw2 b/navit/support/espeak/espeak-data/voices/mb/mb-sw2
index c632e263c..55cfa0d74 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-sw2
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-sw2
@@ -3,5 +3,7 @@ language sv 8
gender female
pitch 140 220
+voicing 130
+
mbrola sw2 sv2_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-sw2-en b/navit/support/espeak/espeak-data/voices/mb/mb-sw2-en
index f2033dc11..015cc9c03 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-sw2-en
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-sw2-en
@@ -1,7 +1,9 @@
name en-swedish-f
-language en
+language en 10
gender female
pitch 140 220
+voicing 130
+
mbrola sw2 sv2_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-tr1 b/navit/support/espeak/espeak-data/voices/mb/mb-tr1
new file mode 100755
index 000000000..97a337d34
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-tr1
@@ -0,0 +1,7 @@
+name turkish-mbrola-1
+language tr 7
+gender male
+
+mbrola tr1 tr1_phtrans
+dictrules 1
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-tr2 b/navit/support/espeak/espeak-data/voices/mb/mb-tr2
new file mode 100755
index 000000000..1dfbf9532
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-tr2
@@ -0,0 +1,10 @@
+name turkish-mbrola-1
+language tr 7
+gender female
+
+pitch 160 230
+voicing 170
+
+mbrola tr2 tr1_phtrans
+dictrules 1
+
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-us1 b/navit/support/espeak/espeak-data/voices/mb/mb-us1
index c62589be7..3dd75260a 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-us1
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-us1
@@ -3,7 +3,7 @@ language en-us
language en 8
gender female
-phonemes en_us
+phonemes en-us
dictrules 3 6
stressLength 170 135 205 205 0 0 245 275
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-us2 b/navit/support/espeak/espeak-data/voices/mb/mb-us2
index d94fce5a4..de799f065 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-us2
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-us2
@@ -3,10 +3,11 @@ language en-us
language en 7
gender male
-phonemes en_us
+phonemes en-us
dictrules 3 6
stressLength 170 135 205 205 0 0 245 275
pitch 82 117
+voicing 80
mbrola us2 us_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-us3 b/navit/support/espeak/espeak-data/voices/mb/mb-us3
index 645e1b7d0..c538b134f 100755
--- a/navit/support/espeak/espeak-data/voices/mb/mb-us3
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-us3
@@ -3,10 +3,11 @@ language en-us
language en 8
gender male
-phonemes en_us
+phonemes en-us
dictrules 3 6
stressLength 170 135 205 205 0 0 245 275
+voicing 150
pitch 82 117
mbrola us3 us3_phtrans
diff --git a/navit/support/espeak/espeak-data/voices/mb/mb-vz1 b/navit/support/espeak/espeak-data/voices/mb/mb-vz1
new file mode 100755
index 000000000..d64a9e5aa
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/mb/mb-vz1
@@ -0,0 +1,11 @@
+language es-vz 7
+language es 8
+name venezuala-mbrola-1
+gender male
+pitch 82 117
+
+mbrola vz1 vz_phtrans
+
+dictrules 3
+replace 00 T s
+voicing 200
diff --git a/navit/support/espeak/espeak-data/voices/af b/navit/support/espeak/espeak-data/voices/other/af
index bcbb2a005..bcbb2a005 100755
--- a/navit/support/espeak/espeak-data/voices/af
+++ b/navit/support/espeak/espeak-data/voices/other/af
diff --git a/navit/support/espeak/espeak-data/voices/en/en-n b/navit/support/espeak/espeak-data/voices/other/en-n
index 933311dad..755bf5f7d 100755
--- a/navit/support/espeak/espeak-data/voices/en/en-n
+++ b/navit/support/espeak/espeak-data/voices/other/en-n
@@ -1,14 +1,15 @@
-name lancashire
+name english-north
language en-uk-north
language en-uk 3
+language en 5
gender male
-phonemes en_n
+phonemes en-n
stressLength 160 150 180 180 220 220 290 290
replace 00 i@3 i@
replace 03 N n
-//replace 03 I i
-//replace 03 I2 i
+replace 03 i I2
+
diff --git a/navit/support/espeak/espeak-data/voices/en/en-rp b/navit/support/espeak/espeak-data/voices/other/en-rp
index 3489f28ad..abc1dbcbf 100755
--- a/navit/support/espeak/espeak-data/voices/en/en-rp
+++ b/navit/support/espeak/espeak-data/voices/other/en-rp
@@ -1,12 +1,13 @@
name english_rp
language en-uk-rp
language en-uk 4
+language en 5
gender male
-phonemes en_rp
+phonemes en-rp
replace 00 o@ O@
replace 00 i@3 i@
replace 03 I i
replace 03 I2 i
-replace 03 @ a2
-replace 03 3 a2
+replace 03 @ a#
+replace 03 3 a#
diff --git a/navit/support/espeak/espeak-data/voices/en/en-sc b/navit/support/espeak/espeak-data/voices/other/en-sc
index e16ae25a8..e0783ec3b 100755
--- a/navit/support/espeak/espeak-data/voices/en/en-sc
+++ b/navit/support/espeak/espeak-data/voices/other/en-sc
@@ -3,7 +3,7 @@ language en-sc
language en 4
gender male
-phonemes en_sc
+phonemes en-sc
dictrules 5 6 7
stressLength 180 130 200 200 0 0 250 270
diff --git a/navit/support/espeak/espeak-data/voices/en/en-wi b/navit/support/espeak/espeak-data/voices/other/en-wi
index 28a42a563..302538de6 100755
--- a/navit/support/espeak/espeak-data/voices/en/en-wi
+++ b/navit/support/espeak/espeak-data/voices/other/en-wi
@@ -1,9 +1,10 @@
name en-westindies
language en-wi
language en-uk 4
+language en 10
gender male
-phonemes en_wi
+phonemes en-wi
dictrules 8
stressLength 175 175 175 175 220 220 250 290
@@ -11,8 +12,8 @@ replace 00 D d
replace 00 T t[
replace 00 U@ o@
replace 00 i@3 i@
-replace 03 @ a2
-replace 03 3 a2
+replace 03 @ a#
+replace 03 3 a#
replace 03 N n
formant 1 98 100 100
diff --git a/navit/support/espeak/espeak-data/voices/en/en-wm b/navit/support/espeak/espeak-data/voices/other/en-wm
index aa82f88fb..33ff4fe41 100755
--- a/navit/support/espeak/espeak-data/voices/en/en-wm
+++ b/navit/support/espeak/espeak-data/voices/other/en-wm
@@ -1,8 +1,10 @@
name english_wmids
language en-uk-wmids
+language en-uk 9
+language en 9
gender male
-phonemes en_wm
+phonemes en-wm
replace 00 h NULL
replace 00 o@ O@
diff --git a/navit/support/espeak/espeak-data/voices/eo b/navit/support/espeak/espeak-data/voices/other/eo
index 36a4bff65..be18c0a7b 100755
--- a/navit/support/espeak/espeak-data/voices/eo
+++ b/navit/support/espeak/espeak-data/voices/other/eo
@@ -1,3 +1,5 @@
name esperanto
language eo
gender male
+
+apostrophe 2
diff --git a/navit/support/espeak/espeak-data/voices/test/grc b/navit/support/espeak/espeak-data/voices/other/grc
index ffa942063..ffa942063 100755
--- a/navit/support/espeak/espeak-data/voices/test/grc
+++ b/navit/support/espeak/espeak-data/voices/other/grc
diff --git a/navit/support/espeak/espeak-data/voices/other/jbo b/navit/support/espeak/espeak-data/voices/other/jbo
new file mode 100755
index 000000000..f43ef84fa
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/other/jbo
@@ -0,0 +1,4 @@
+name lojban
+language jbo
+
+speed 80 // speed adjustment, percentage
diff --git a/navit/support/espeak/espeak-data/voices/la b/navit/support/espeak/espeak-data/voices/other/la
index f3e97b523..f3e97b523 100644..100755
--- a/navit/support/espeak/espeak-data/voices/la
+++ b/navit/support/espeak/espeak-data/voices/other/la
diff --git a/navit/support/espeak/espeak-data/voices/other/lfn b/navit/support/espeak/espeak-data/voices/other/lfn
new file mode 100755
index 000000000..5c814a121
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/other/lfn
@@ -0,0 +1,6 @@
+name lingua_franca_nova
+language lfn
+gender male
+
+phonemes base2
+l_unpronouncable 0
diff --git a/navit/support/espeak/espeak-data/voices/sw b/navit/support/espeak/espeak-data/voices/other/sw
index cf584b7dd..57db0e57d 100755
--- a/navit/support/espeak/espeak-data/voices/sw
+++ b/navit/support/espeak/espeak-data/voices/other/sw
@@ -1,4 +1,4 @@
-name swahihi-test
+name swahili-test
language sw
gender male
diff --git a/navit/support/espeak/espeak-data/voices/pt b/navit/support/espeak/espeak-data/voices/pt
index 53cb31446..10d62711a 100755
--- a/navit/support/espeak/espeak-data/voices/pt
+++ b/navit/support/espeak/espeak-data/voices/pt
@@ -1,7 +1,8 @@
name brazil
-language pt
language pt-br
+language pt
gender male
dictrules 2
+stressLength 200 115 230 230 0 0 250 270
diff --git a/navit/support/espeak/espeak-data/voices/test/am b/navit/support/espeak/espeak-data/voices/test/am
new file mode 100755
index 000000000..729d1f07e
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/am
@@ -0,0 +1,3 @@
+name amharic-test
+language am
+
diff --git a/navit/support/espeak/espeak-data/voices/test/as b/navit/support/espeak/espeak-data/voices/test/as
new file mode 100755
index 000000000..109f92c82
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/as
@@ -0,0 +1,4 @@
+name assamese-test
+language as
+
+ \ No newline at end of file
diff --git a/navit/support/espeak/espeak-data/voices/test/az b/navit/support/espeak/espeak-data/voices/test/az
new file mode 100755
index 000000000..6fdf38274
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/az
@@ -0,0 +1,3 @@
+name azerbaijani-test
+language az
+
diff --git a/navit/support/espeak/espeak-data/voices/test/bn b/navit/support/espeak/espeak-data/voices/test/bn
new file mode 100755
index 000000000..e1e569fbe
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/bn
@@ -0,0 +1,3 @@
+name bengali-test
+language bn
+gender male
diff --git a/navit/support/espeak/espeak-data/voices/test/eu b/navit/support/espeak/espeak-data/voices/test/eu
new file mode 100755
index 000000000..35ada0cf4
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/eu
@@ -0,0 +1,3 @@
+name basque-test
+language eu
+
diff --git a/navit/support/espeak/espeak-data/voices/test/gd b/navit/support/espeak/espeak-data/voices/test/gd
new file mode 100644
index 000000000..9f8ed6123
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/gd
@@ -0,0 +1,3 @@
+name scottish-gaelic-test
+language gd
+
diff --git a/navit/support/espeak/espeak-data/voices/test/gu b/navit/support/espeak/espeak-data/voices/test/gu
new file mode 100755
index 000000000..4d983c68d
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/gu
@@ -0,0 +1,3 @@
+name gujarati-test
+language gu
+
diff --git a/navit/support/espeak/espeak-data/voices/test/jbo b/navit/support/espeak/espeak-data/voices/test/jbo
deleted file mode 100644
index ebab1a875..000000000
--- a/navit/support/espeak/espeak-data/voices/test/jbo
+++ /dev/null
@@ -1,3 +0,0 @@
-name lojban
-language jbo
-
diff --git a/navit/support/espeak/espeak-data/voices/test/kl b/navit/support/espeak/espeak-data/voices/test/kl
new file mode 100755
index 000000000..1ee5f0b5f
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/kl
@@ -0,0 +1,3 @@
+name greenlandic
+language kl
+
diff --git a/navit/support/espeak/espeak-data/voices/test/ko b/navit/support/espeak/espeak-data/voices/test/ko
new file mode 100755
index 000000000..d9330a8ff
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/ko
@@ -0,0 +1,6 @@
+name korean-test
+language ko
+gender male
+pitch 80 118
+intonation 2
+
diff --git a/navit/support/espeak/espeak-data/voices/test/nci b/navit/support/espeak/espeak-data/voices/test/nci
new file mode 100755
index 000000000..6ce7eee49
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/nci
@@ -0,0 +1,7 @@
+name nahuatl-classical
+language nci
+gender male
+
+intonation 3
+stressrule 2
+stressLength 190 190 200 200 0 0 220 240
diff --git a/navit/support/espeak/espeak-data/voices/test/or b/navit/support/espeak/espeak-data/voices/test/or
new file mode 100755
index 000000000..b3dec7161
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/or
@@ -0,0 +1,2 @@
+name oriya-test
+language or
diff --git a/navit/support/espeak/espeak-data/voices/test/pap b/navit/support/espeak/espeak-data/voices/test/pap
index 3b105a7fb..3b105a7fb 100644..100755
--- a/navit/support/espeak/espeak-data/voices/test/pap
+++ b/navit/support/espeak/espeak-data/voices/test/pap
diff --git a/navit/support/espeak/espeak-data/voices/test/si b/navit/support/espeak/espeak-data/voices/test/si
new file mode 100755
index 000000000..87d564d67
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/si
@@ -0,0 +1,4 @@
+name sinhala-test
+language si
+
+intonation 2
diff --git a/navit/support/espeak/espeak-data/voices/test/sl b/navit/support/espeak/espeak-data/voices/test/sl
new file mode 100755
index 000000000..6044e9d04
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/sl
@@ -0,0 +1,3 @@
+name slovenian-test
+language sl
+ \ No newline at end of file
diff --git a/navit/support/espeak/espeak-data/voices/test/te b/navit/support/espeak/espeak-data/voices/test/te
new file mode 100755
index 000000000..c23078a86
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/te
@@ -0,0 +1,5 @@
+name telugu-test
+language te
+
+intonation 2
+//consonants 80
diff --git a/navit/support/espeak/espeak-data/voices/test/ur b/navit/support/espeak/espeak-data/voices/test/ur
new file mode 100755
index 000000000..d4d113c4c
--- /dev/null
+++ b/navit/support/espeak/espeak-data/voices/test/ur
@@ -0,0 +1,5 @@
+name urdu-test
+language ur
+
+stressrule 6
+
diff --git a/navit/support/espeak/espeak-data/zh_dict b/navit/support/espeak/espeak-data/zh_dict
index 0b611f845..29db8608e 100644
--- a/navit/support/espeak/espeak-data/zh_dict
+++ b/navit/support/espeak/espeak-data/zh_dict
Binary files differ
diff --git a/navit/support/espeak/espeak-data/zhy_dict b/navit/support/espeak/espeak-data/zhy_dict
index 4ad9e7c21..e4f85571a 100644
--- a/navit/support/espeak/espeak-data/zhy_dict
+++ b/navit/support/espeak/espeak-data/zhy_dict
Binary files differ
diff --git a/navit/support/espeak/espeak.c b/navit/support/espeak/espeak.c
index 7997d0673..028650272 100644
--- a/navit/support/espeak/espeak.c
+++ b/navit/support/espeak/espeak.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2006 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2006 to 2013 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -20,10 +20,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
+#ifndef NEED_GETOPT
#include <getopt.h>
+#include <stdbool.h> //for "true"
+#endif
#include <time.h>
#include <sys/stat.h>
+#include "speech.h"
#include "speak_lib.h"
// This version of the command-line speak program uses the
@@ -32,51 +37,55 @@
static const char *help_text =
-"\nspeak [options] [\"<words>\"]\n\n"
+"\nespeak [options] [\"<words>\"]\n\n"
"-f <text file> Text file to speak\n"
"--stdin Read text input from stdin instead of a file\n\n"
-"If neither -f nor --stdin, <words> are spoken, or if none then text is\n"
-"spoken from stdin, each line separately.\n\n"
+"If neither -f nor --stdin, then <words> are spoken, or if none then text\n"
+"is spoken from stdin, each line separately.\n\n"
"-a <integer>\n"
"\t Amplitude, 0 to 200, default is 100\n"
"-g <integer>\n"
"\t Word gap. Pause between words, units of 10mS at the default speed\n"
+"-k <integer>\n"
+"\t Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
+"\t higher values indicate a pitch increase (try -k20).\n"
"-l <integer>\n"
"\t Line length. If not zero (which is the default), consider\n"
"\t lines less than this length as end-of-clause\n"
"-p <integer>\n"
"\t Pitch adjustment, 0 to 99, default is 50\n"
"-s <integer>\n"
-"\t Speed in words per minute, 80 to 390, default is 170\n"
+"\t Speed in words per minute, 80 to 450, default is 175\n"
"-v <voice name>\n"
"\t Use voice file of this name from espeak-data/voices\n"
"-w <wave file name>\n"
-"\t Write output to this WAV file, rather than speaking it directly\n"
+"\t Write speech to this WAV file, rather than speaking it directly\n"
"-b\t Input text encoding, 1=UTF8, 2=8 bit, 4=16 bit \n"
"-m\t Interpret SSML markup, and ignore other < > tags\n"
"-q\t Quiet, don't produce any speech (may be useful with -x)\n"
"-x\t Write phoneme mnemonics to stdout\n"
"-X\t Write phonemes mnemonics and translation trace to stdout\n"
"-z\t No final sentence pause at the end of the text\n"
-"--stdout Write speech output to stdout\n"
"--compile=<voice name>\n"
-"\t Compile the pronunciation rules and dictionary in the current\n"
-"\t directory. =<voice name> is optional and specifies which language\n"
+"\t Compile pronunciation rules and dictionary from the current\n"
+"\t directory. <voice name> specifies the language\n"
+"--ipa Write phonemes to stdout using International Phonetic Alphabet\n"
+"\t --ipa=1 Use ties, --ipa=2 Use ZWJ, --ipa=3 Separate with _\n"
"--path=\"<path>\"\n"
"\t Specifies the directory containing the espeak-data directory\n"
+"--pho Write mbrola phoneme data (.pho) to stdout or to the file in --phonout\n"
"--phonout=\"<filename>\"\n"
-"\t Write output from -x -X commands and mbrola phoneme data to this file\n"
+"\t Write phoneme output from -x -X --ipa and --pho to this file\n"
"--punct=\"<characters>\"\n"
"\t Speak the names of punctuation characters during speaking. If\n"
"\t =<characters> is omitted, all punctuation is spoken.\n"
"--split=\"<minutes>\"\n"
"\t Starts a new WAV file every <minutes>. Used with -w\n"
+"--stdout Write speech output to stdout\n"
+"--version Shows version number and date, and location of espeak-data\n"
"--voices=<language>\n"
"\t List the available voices for the specified language.\n"
-"\t If <language> is omitted, then list all voices.\n"
-"-k <integer>\n"
-"\t Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
-"\t higher values = a pitch increase (try -k20).\n";
+"\t If <language> is omitted, then list all voices.\n";
@@ -85,6 +94,7 @@ int samplerate;
int quiet = 0;
unsigned int samples_total = 0;
unsigned int samples_split = 0;
+unsigned int samples_split_seconds = 0;
unsigned int wavefile_count = 0;
FILE *f_wavfile = NULL;
@@ -116,20 +126,22 @@ void strncpy0(char *dest, const char *source, int size)
}
-void DisplayVoices(FILE *f_out, char *language)
+static void DisplayVoices(FILE *f_out, char *language)
{//============================================
int ix;
const char *p;
int len;
int count;
- int scores = 0;
+ int c;
+ int j;
const espeak_VOICE *v;
const char *lang_name;
char age_buf[12];
+ char buf[80];
const espeak_VOICE **voices;
espeak_VOICE voice_select;
- static char genders[4] = {' ','M','F',' '};
+ static char genders[4] = {'-','M','F','-'};
if((language != NULL) && (language[0] != 0))
{
@@ -139,14 +151,13 @@ void DisplayVoices(FILE *f_out, char *language)
voice_select.gender = 0;
voice_select.name = NULL;
voices = espeak_ListVoices(&voice_select);
- scores = 1;
}
else
{
voices = espeak_ListVoices(NULL);
}
- fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Langs\n");
+ fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Languages\n");
for(ix=0; (v = voices[ix]) != NULL; ix++)
{
@@ -164,8 +175,16 @@ void DisplayVoices(FILE *f_out, char *language)
if(count==0)
{
- fprintf(f_out,"%2d %-12s%s%c %-17s %-11s ",
- p[0],lang_name,age_buf,genders[v->gender],v->name,v->identifier);
+ for(j=0; j < sizeof(buf); j++)
+ {
+ // replace spaces in the name
+ if((c = v->name[j]) == ' ')
+ c = '_';
+ if((buf[j] = c) == 0)
+ break;
+ }
+ fprintf(f_out,"%2d %-12s%s%c %-20s %-13s ",
+ p[0],lang_name,age_buf,genders[v->gender],buf,v->identifier);
}
else
{
@@ -174,8 +193,6 @@ void DisplayVoices(FILE *f_out, char *language)
count++;
p += len+2;
}
-// if(scores)
-// fprintf(f_out,"%3d ",v->score);
fputc('\n',f_out);
}
} // end of DisplayVoices
@@ -197,7 +214,7 @@ static void Write4Bytes(FILE *f, int value)
-int OpenWavFile(char *path, int rate)
+static int OpenWavFile(char *path, int rate)
//===================================
{
static unsigned char wave_hdr[44] = {
@@ -208,28 +225,34 @@ int OpenWavFile(char *path, int rate)
if(path == NULL)
return(2);
- if(path[0] == 0)
- return(0);
-
- if(strcmp(path,"stdout")==0)
- f_wavfile = stdout;
- else
- f_wavfile = fopen(path,"wb");
+ while(isspace(*path)) path++;
- if(f_wavfile != NULL)
+ f_wavfile = NULL;
+ if(path[0] != 0)
{
- fwrite(wave_hdr,1,24,f_wavfile);
- Write4Bytes(f_wavfile,rate);
- Write4Bytes(f_wavfile,rate * 2);
- fwrite(&wave_hdr[32],1,12,f_wavfile);
- return(0);
+ if(strcmp(path,"stdout")==0)
+ f_wavfile = stdout;
+ else
+ f_wavfile = fopen(path,"wb");
+ }
+
+ if(f_wavfile == NULL)
+ {
+ fprintf(stderr,"Can't write to: '%s'\n",path);
+ return(1);
}
- return(1);
+
+
+ fwrite(wave_hdr,1,24,f_wavfile);
+ Write4Bytes(f_wavfile,rate);
+ Write4Bytes(f_wavfile,rate * 2);
+ fwrite(&wave_hdr[32],1,12,f_wavfile);
+ return(0);
} // end of OpenWavFile
-static void CloseWavFile()
+static void CloseWavFile(void)
//========================
{
unsigned int pos;
@@ -264,25 +287,40 @@ static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
return(0);
}
- if(samples_split > 0)
+ while(events->type != 0)
{
- // start a new WAV file when this limit is reached, at the next sentence boundary
- while(events->type != 0)
+ if(events->type == espeakEVENT_SAMPLERATE)
{
- if((events->type == espeakEVENT_SENTENCE) && (samples_total > samples_split))
+ samplerate = events->id.number;
+ samples_split = samples_split_seconds * samplerate;
+ }
+ else
+ if(events->type == espeakEVENT_SENTENCE)
+ {
+ // start a new WAV file when the limit is reached, at this sentence boundary
+ if((samples_split > 0) && (samples_total > samples_split))
{
CloseWavFile();
samples_total = 0;
+ wavefile_count++;
}
- events++;
}
+ events++;
}
if(f_wavfile == NULL)
{
- sprintf(fname,"%s_%.2d%s",wavefile,++wavefile_count,filetype);
- if(OpenWavFile(fname, samplerate) != 0)
- return(1);
+ if(samples_split > 0)
+ {
+ sprintf(fname,"%s_%.2d%s",wavefile,wavefile_count+1,filetype);
+ if(OpenWavFile(fname, samplerate) != 0)
+ return(1);
+ }
+ else
+ {
+ if(OpenWavFile(wavefile, samplerate) != 0)
+ return(1);
+ }
}
if(numsamples > 0)
@@ -294,6 +332,32 @@ static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
}
+static void PrintVersion(void)
+{//=======================
+ const char *version;
+ const char *path_data;
+ espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, NULL, espeakINITIALIZE_DONT_EXIT);
+ version = espeak_Info(&path_data);
+ printf("eSpeak text-to-speech: %s Data at: %s\n", version, path_data);
+}
+
+
+
+#ifdef NEED_GETOPT
+ struct option {
+ char *name;
+ int has_arg;
+ int *flag;
+ int val;
+ };
+ int optind;
+ static int optional_argument;
+ static const char *arg_opts = "abfgklpsvw"; // which options have arguments
+ static char *opt_string="";
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+#endif
int main (int argc, char **argv)
//==============================
@@ -315,7 +379,10 @@ int main (int argc, char **argv)
{"stdout", no_argument, 0, 0x105},
{"split", optional_argument, 0, 0x106},
{"path", required_argument, 0, 0x107},
- {"phonout", required_argument, 0, 0x108},
+ {"phonout", required_argument, 0, 0x108},
+ {"pho", no_argument, 0, 0x109},
+ {"ipa", optional_argument, 0, 0x10a},
+ {"version", no_argument, 0, 0x10b},
{0, 0, 0, 0}
};
@@ -330,6 +397,7 @@ int main (int argc, char **argv)
int option_index = 0;
int c;
int ix;
+ char *optarg2;
int value;
int flag_stdin = 0;
int flag_compile = 0;
@@ -342,25 +410,78 @@ int main (int argc, char **argv)
int wordgap = -1;
int option_capitals = -1;
int option_punctuation = -1;
- int option_phonemes = -1;
+ int option_phonemes = 0;
+ int option_mbrola_phonemes = 0;
int option_linelength = 0;
int option_waveout = 0;
espeak_VOICE voice_select;
char filename[200];
char voicename[40];
- char voice_mbrola[20];
- char dictname[40];
#define N_PUNCTLIST 100
wchar_t option_punctlist[N_PUNCTLIST];
voicename[0] = 0;
- voice_mbrola[0] = 0;
- dictname[0] = 0;
wavefile[0] = 0;
filename[0] = 0;
option_punctlist[0] = 0;
+#ifdef NEED_GETOPT
+ optind = 1;
+ opt_string = "";
+ while(optind < argc)
+ {
+ int len;
+ char *p;
+
+ if((c = *opt_string) == 0)
+ {
+ opt_string = argv[optind];
+ if(opt_string[0] != '-')
+ break;
+
+ optind++;
+ opt_string++;
+ c = *opt_string;
+ }
+ opt_string++;
+ p = optarg2 = opt_string;
+
+ if(c == '-')
+ {
+ if(p[0] == 0)
+ break; // -- means don't interpret further - as commands
+
+ opt_string="";
+ for(ix=0; ;ix++)
+ {
+ if(long_options[ix].name == 0)
+ break;
+ len = strlen(long_options[ix].name);
+ if(memcmp(long_options[ix].name,p,len)==0)
+ {
+ c = long_options[ix].val;
+ optarg2 = NULL;
+
+ if((long_options[ix].has_arg != 0) && (p[len]=='='))
+ {
+ optarg2 = &p[len+1];
+ }
+ break;
+ }
+ }
+ }
+ else
+ if(strchr(arg_opts,c) != NULL)
+ {
+ opt_string="";
+ if(optarg2[0]==0)
+ {
+ // the option's value is in the next argument
+ optarg2 = argv[optind++];
+ }
+ }
+#else
while(true)
{
c = getopt_long (argc, argv, "a:b:f:g:hk:l:mp:qs:v:w:xXz",
@@ -369,12 +490,14 @@ int main (int argc, char **argv)
/* Detect the end of the options. */
if (c == -1)
break;
+ optarg2 = optarg;
+#endif
switch (c)
{
case 'b':
// input character encoding, 8bit, 16bit, UTF8
- if((sscanf(optarg,"%d",&value) == 1) && (value <= 4))
+ if((sscanf(optarg2,"%d",&value) == 1) && (value <= 4))
synth_flags |= value;
else
synth_flags |= espeakCHARS_8BIT;
@@ -382,12 +505,13 @@ int main (int argc, char **argv)
case 'h':
printf("\n");
- printf("eSpeak text-to-speech: %s\n%s",espeak_Info(),help_text);
+ PrintVersion();
+ printf("%s", help_text);
exit(0);
break;
case 'k':
- option_capitals = atoi(optarg);
+ option_capitals = atoi(optarg2);
break;
case 'x':
@@ -403,7 +527,7 @@ int main (int argc, char **argv)
break;
case 'p':
- pitch = atoi(optarg);
+ pitch = atoi(optarg2);
break;
case 'q':
@@ -411,32 +535,32 @@ int main (int argc, char **argv)
break;
case 'f':
- strncpy0(filename,optarg,sizeof(filename));
+ strncpy0(filename,optarg2,sizeof(filename));
break;
case 'l':
- option_linelength = atoi(optarg);
+ option_linelength = atoi(optarg2);
break;
case 'a':
- volume = atoi(optarg);
+ volume = atoi(optarg2);
break;
case 's':
- speed = atoi(optarg);
+ speed = atoi(optarg2);
break;
case 'g':
- wordgap = atoi(optarg);
+ wordgap = atoi(optarg2);
break;
case 'v':
- strncpy0(voicename,optarg,sizeof(voicename));
+ strncpy0(voicename,optarg2,sizeof(voicename));
break;
case 'w':
option_waveout = 1;
- strncpy0(wavefile,optarg,sizeof(filename));
+ strncpy0(wavefile,optarg2,sizeof(filename));
break;
case 'z': // remove pause from the end of a sentence
@@ -454,17 +578,17 @@ int main (int argc, char **argv)
case 0x101: // --compile-debug
case 0x102: // --compile
- strncpy0(voicename,optarg,sizeof(voicename));
+ strncpy0(voicename,optarg2,sizeof(voicename));
flag_compile = c;
quiet = 1;
break;
case 0x103: // --punct
option_punctuation = 1;
- if(optarg != NULL)
+ if(optarg2 != NULL)
{
ix = 0;
- while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg[ix]) != 0)) ix++;
+ while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++;
option_punctlist[N_PUNCTLIST-1] = 0;
option_punctuation = 2;
}
@@ -472,27 +596,50 @@ int main (int argc, char **argv)
case 0x104: // --voices
espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,data_path,0);
- DisplayVoices(stdout,optarg);
+ DisplayVoices(stdout,optarg2);
exit(0);
case 0x106: // -- split
- if(optarg == NULL)
- samples_split = 30; // default 30 minutes
+ if(optarg2 == NULL)
+ samples_split_seconds = 30 * 60; // default 30 minutes
else
- samples_split = atoi(optarg);
+ samples_split_seconds = atoi(optarg2) * 60;
break;
case 0x107: // --path
- data_path = optarg;
+ data_path = optarg2;
break;
case 0x108: // --phonout
- if((f_phonemes_out = fopen(optarg,"w")) == NULL)
+ if((f_phonemes_out = fopen(optarg2,"w")) == NULL)
+ {
+ fprintf(stderr,"Can't write to: %s\n",optarg2);
+ }
+ break;
+
+ case 0x109: // --pho
+ option_mbrola_phonemes = 16;
+ break;
+
+ case 0x10a: // --ipa
+ option_phonemes = 3;
+ if(optarg2 != NULL)
{
- fprintf(stderr,"Can't write to: %s\n",optarg);
+ value = -1;
+ sscanf(optarg2,"%d",&value);
+ if((value<0) || (value>3))
+ {
+ fprintf(stderr,"Bad value for -ipa=\n");
+ value = 0;
+ }
+ option_phonemes += value;
}
break;
+ case 0x10b: // -version
+ PrintVersion();
+ exit(0);
+
default:
exit(0);
}
@@ -503,7 +650,7 @@ int main (int argc, char **argv)
{
// writing to a file (or no output), we can use synchronous mode
samplerate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,0,data_path,0);
- samples_split = (samplerate * samples_split) * 60;
+ samples_split = samplerate * samples_split_seconds;
espeak_SetSynthCallback(SynthCallback);
if(samples_split)
@@ -516,12 +663,6 @@ int main (int argc, char **argv)
*extn = 0;
}
}
- else
- if(option_waveout)
- {
- if(OpenWavFile(wavefile,samplerate) != 0)
- exit(4);
- }
}
else
{
@@ -568,7 +709,7 @@ int main (int argc, char **argv)
espeak_SetParameter(espeakLINELENGTH,option_linelength,0);
if(option_punctuation == 2)
espeak_SetPunctuationList(option_punctlist);
- espeak_SetPhonemeTrace(option_phonemes,f_phonemes_out);
+ espeak_SetPhonemeTrace(option_phonemes | option_mbrola_phonemes,f_phonemes_out);
if(filename[0]==0)
{
@@ -651,13 +792,18 @@ int main (int argc, char **argv)
exit(3);
}
- fread(p_text,1,filesize,f_text);
+ if(fread(p_text,1,filesize,f_text) <= 0)
+ fprintf(stderr, "unable to read from file\n");
p_text[filesize]=0;
espeak_Synth(p_text,filesize+1,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
fclose(f_text);
}
- espeak_Synchronize();
+ if(espeak_Synchronize() != EE_OK)
+ {
+ fprintf(stderr, "espeak_Synchronize() failed, maybe error when opening output device\n");
+ exit(4);
+ }
if(f_phonemes_out != stdout)
fclose(f_phonemes_out); // needed for WinCE
diff --git a/navit/support/espeak/espeak_command.c b/navit/support/espeak/espeak_command.c
index 1b59333c1..d8460c75a 100644
--- a/navit/support/espeak/espeak_command.c
+++ b/navit/support/espeak/espeak_command.c
@@ -46,14 +46,14 @@ t_espeak_command* create_espeak_text(const void *text, size_t size, unsigned int
{
goto text_error;
}
-
- a_text = malloc( size );
+
+ a_text = malloc( size+1 );
if (!a_text)
{
goto text_error;
}
memcpy(a_text, text, size);
-
+
a_command->type = ET_TEXT;
a_command->state = CS_UNDEFINED;
data = &(a_command->u.my_text);
@@ -102,7 +102,7 @@ t_espeak_command* create_espeak_terminated_msg(unsigned int unique_identifier, v
{
goto msg_error;
}
-
+
a_command->type = ET_TERMINATED_MSG;
a_command->state = CS_UNDEFINED;
data = &(a_command->u.my_terminated_msg);
@@ -234,7 +234,7 @@ t_espeak_command* create_espeak_char(wchar_t character, void* user_data)
{
goto char_error;
}
-
+
a_command->type = ET_CHAR;
a_command->state = CS_UNDEFINED;
a_command->u.my_char.user_data = user_data;
@@ -270,11 +270,11 @@ t_espeak_command* create_espeak_parameter(espeak_PARAMETER parameter, int value,
{
goto param_error;
}
-
+
a_command->type = ET_PARAMETER;
a_command->state = CS_UNDEFINED;
data = &(a_command->u.my_param);
- data->parameter = parameter;
+ data->parameter = parameter;
data->value = value;
data->relative = relative;
a_error=0;
@@ -308,7 +308,7 @@ t_espeak_command* create_espeak_punctuation_list(const wchar_t *punctlist)
{
goto list_error;
}
-
+
a_command->type = ET_PUNCTUATION_LIST;
a_command->state = CS_UNDEFINED;
@@ -350,7 +350,7 @@ t_espeak_command* create_espeak_voice_name(const char *name)
{
goto name_error;
}
-
+
a_command->type = ET_VOICE_NAME;
a_command->state = CS_UNDEFINED;
a_command->u.my_voice_name = strdup( name);
@@ -381,7 +381,7 @@ t_espeak_command* create_espeak_voice_spec(espeak_VOICE *voice)
{
goto spec_error;
}
-
+
a_command->type = ET_VOICE_SPEC;
a_command->state = CS_UNDEFINED;
{
@@ -451,11 +451,11 @@ int delete_espeak_command( t_espeak_command* the_command)
break;
case ET_TERMINATED_MSG:
- {
+ {
// if the terminated msg is pending,
- // it must be processed here for informing the calling program
+ // it must be processed here for informing the calling program
// that its message is finished.
- // This can be important for cleaning the related user data.
+ // This can be important for cleaning the related user data.
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg);
if (the_command->state == CS_PENDING)
{
@@ -491,7 +491,7 @@ int delete_espeak_command( t_espeak_command* the_command)
free((void*)(the_command->u.my_voice_name));
}
break;
-
+
case ET_VOICE_SPEC:
{
espeak_VOICE* data = &(the_command->u.my_voice_spec);
@@ -542,23 +542,23 @@ void process_espeak_command( t_espeak_command* the_command)
case ET_TEXT:
{
t_espeak_text* data = &(the_command->u.my_text);
- sync_espeak_Synth( data->unique_identifier, data->text, data->size,
- data->position, data->position_type,
- data->end_position, data->flags, data->user_data);
+ sync_espeak_Synth( data->unique_identifier, data->text, data->size,
+ data->position, data->position_type,
+ data->end_position, data->flags, data->user_data);
}
break;
case ET_MARK:
{
t_espeak_mark* data = &(the_command->u.my_mark);
- sync_espeak_Synth_Mark( data->unique_identifier, data->text, data->size,
- data->index_mark, data->end_position, data->flags,
+ sync_espeak_Synth_Mark( data->unique_identifier, data->text, data->size,
+ data->index_mark, data->end_position, data->flags,
data->user_data);
}
break;
case ET_TERMINATED_MSG:
- {
+ {
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg);
sync_espeak_terminated_msg( data->unique_identifier, data->user_data);
}
@@ -645,7 +645,7 @@ void display_espeak_command( t_espeak_command* the_command)
case ET_KEY:
{
- const char* data = the_command->u.my_key;
+ const char* data = the_command->u.my_key.key_name;
SHOW("display_espeak_command > (0x%x) KEY=%c\n", the_command, data);
}
break;
@@ -654,15 +654,15 @@ void display_espeak_command( t_espeak_command* the_command)
{
t_espeak_terminated_msg* data = &(the_command->u.my_terminated_msg);
- SHOW("display_espeak_command > (0x%x) TERMINATED_MSG uid=%d, user_data=0x%x, state=%d\n",
- the_command, data->unique_identifier, data->user_data,
+ SHOW("display_espeak_command > (0x%x) TERMINATED_MSG uid=%d, user_data=0x%x, state=%d\n",
+ the_command, data->unique_identifier, data->user_data,
the_command->state);
}
break;
case ET_CHAR:
{
- const wchar_t data = the_command->u.my_char;
+ const wchar_t data = the_command->u.my_char.character;
SHOW("display_espeak_command > (0x%x) CHAR=%c\n", the_command, (char)data);
}
break;
@@ -670,7 +670,7 @@ void display_espeak_command( t_espeak_command* the_command)
case ET_PARAMETER:
{
t_espeak_parameter* data = &(the_command->u.my_param);
- SHOW("display_espeak_command > (0x%x) PARAMETER=%d, value=%d, relative=%d\n",
+ SHOW("display_espeak_command > (0x%x) PARAMETER=%d, value=%d, relative=%d\n",
the_command, data->parameter, data->value, data->relative);
}
break;
diff --git a/navit/support/espeak/espeak_command.h b/navit/support/espeak/espeak_command.h
index b2664be6a..5a6592f39 100644
--- a/navit/support/espeak/espeak_command.h
+++ b/navit/support/espeak/espeak_command.h
@@ -19,7 +19,7 @@ enum t_espeak_type
ET_TERMINATED_MSG
};
-typedef struct
+typedef struct
{
unsigned int unique_identifier;
void* text;
@@ -31,7 +31,7 @@ typedef struct
void* user_data;
} t_espeak_text;
-typedef struct
+typedef struct
{
unsigned int unique_identifier;
void* text;
@@ -42,53 +42,53 @@ typedef struct
void* user_data;
} t_espeak_mark;
-typedef struct
+typedef struct
{
unsigned int unique_identifier;
void* user_data;
- wchar_t character;
+ wchar_t character;
} t_espeak_character;
-typedef struct
+typedef struct
{
unsigned int unique_identifier;
void* user_data;
- const char* key_name;
+ const char* key_name;
} t_espeak_key;
-typedef struct
+typedef struct
{
unsigned int unique_identifier;
void* user_data;
} t_espeak_terminated_msg;
-typedef struct
+typedef struct
{
- espeak_PARAMETER parameter;
- int value;
+ espeak_PARAMETER parameter;
+ int value;
int relative;
} t_espeak_parameter;
-enum t_command_state
+enum t_command_state
{
CS_UNDEFINED, // The command has just been created
CS_PENDING, // stored in the fifo
CS_PROCESSED // processed
};
-typedef struct
+typedef struct
{
enum t_espeak_type type;
- enum t_command_state state;
+ enum t_command_state state;
union command
{
- t_espeak_text my_text;
- t_espeak_mark my_mark;
- t_espeak_key my_key;
- t_espeak_character my_char;
+ t_espeak_text my_text;
+ t_espeak_mark my_mark;
+ t_espeak_key my_key;
+ t_espeak_character my_char;
t_espeak_parameter my_param;
const wchar_t* my_punctuation_list;
const char *my_voice_name;
@@ -123,11 +123,11 @@ int delete_espeak_command( t_espeak_command* the_command);
void display_espeak_command(t_espeak_command* the_command);
-espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
- unsigned int position, espeak_POSITION_TYPE position_type,
+espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
+ unsigned int position, espeak_POSITION_TYPE position_type,
unsigned int end_position, unsigned int flags, void* user_data);
-espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size,
- const char *index_mark, unsigned int end_position,
+espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size,
+ const char *index_mark, unsigned int end_position,
unsigned int flags, void* user_data);
void sync_espeak_Key(const char *key);
void sync_espeak_Char(wchar_t character);
diff --git a/navit/support/espeak/event.c b/navit/support/espeak/event.c
index cc696272d..f41c65950 100644
--- a/navit/support/espeak/event.c
+++ b/navit/support/espeak/event.c
@@ -24,7 +24,9 @@
//<includes
+#ifndef PLATFORM_WINDOWS
#include <unistd.h>
+#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
@@ -41,20 +43,21 @@
//<decls and function prototypes
-// my_mutex: protects my_thread_is_talking,
+// my_mutex: protects my_thread_is_talking,
static pthread_mutex_t my_mutex;
static sem_t my_sem_start_is_required;
static sem_t my_sem_stop_is_required;
static sem_t my_sem_stop_is_acknowledged;
// my_thread: polls the audio duration and compares it to the duration of the first event.
static pthread_t my_thread;
+static bool thread_inited;
static t_espeak_callback* my_callback = NULL;
static int my_event_is_running=0;
enum {MIN_TIMEOUT_IN_MS=10,
ACTIVITY_TIMEOUT=50, // in ms, check that the stream is active
- MAX_ACTIVITY_CHECK=6
+ MAX_ACTIVITY_CHECK=6
};
@@ -94,17 +97,17 @@ void event_init(void)
assert(-1 != sem_init(&my_sem_stop_is_required, 0, 0));
assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0));
- pthread_attr_t a_attrib;
- if (pthread_attr_init (& a_attrib)
- || pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE)
- || pthread_create( &my_thread,
- & a_attrib,
- polling_thread,
- (void*)NULL))
- {
- assert(0);
- }
-
+ pthread_attr_t a_attrib;
+
+ if (pthread_attr_init (&a_attrib) == 0
+ && pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE) == 0)
+ {
+ thread_inited = (0 == pthread_create(&my_thread,
+ &a_attrib,
+ polling_thread,
+ (void*)NULL));
+ }
+ assert(thread_inited);
pthread_attr_destroy(&a_attrib);
}
//>
@@ -127,7 +130,10 @@ ENTER("event_display");
"MARK",
"PLAY",
"END",
- "MSG_TERMINATED"
+ "MSG_TERMINATED",
+ "PHONEME",
+ "SAMPLERATE",
+ "??"
};
SHOW("event_display > event=0x%x\n",event);
@@ -147,17 +153,17 @@ ENTER("event_display");
static espeak_EVENT* event_copy (espeak_EVENT* event)
{
ENTER("event_copy");
-
+
if (event==NULL)
{
return NULL;
}
-
+
espeak_EVENT* a_event=(espeak_EVENT*)malloc(sizeof(espeak_EVENT));
if (a_event)
{
memcpy(a_event, event, sizeof(espeak_EVENT));
-
+
switch(event->type)
{
case espeakEVENT_MARK:
@@ -167,14 +173,14 @@ static espeak_EVENT* event_copy (espeak_EVENT* event)
a_event->id.name = strdup(event->id.name);
}
break;
-
+
default:
break;
}
}
-
+
event_display(a_event);
-
+
return a_event;
}
@@ -288,7 +294,7 @@ ENTER("event_declare");
return EE_INTERNAL_ERROR;
}
- int a_status = pthread_mutex_lock(&my_mutex);
+ int a_status = pthread_mutex_lock(&my_mutex);
espeak_ERROR a_error = EE_OK;
if (!a_status)
@@ -310,7 +316,7 @@ ENTER("event_declare");
//
// if (!a_status && !my_event_is_running && (a_error == EE_OK))
// {
-// // quit when command is actually started
+// // quit when command is actually started
// // (for possible forthcoming 'end of command' checks)
SHOW_TIME("event_declare > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required);
@@ -386,28 +392,32 @@ static int sleep_until_timeout_or_stop_request(uint32_t time_in_ms)
ENTER("sleep_until_timeout_or_stop_request");
int a_stop_is_required=0;
- struct timespec ts, to;
+ struct timespec ts;
struct timeval tv;
int err=0;
clock_gettime2( &ts);
+
+#ifdef DEBUG_ENABLED
+ struct timespec to;
to.tv_sec = ts.tv_sec;
to.tv_nsec = ts.tv_nsec;
+#endif
add_time_in_ms( &ts, time_in_ms);
- SHOW("polling_thread > sleep_until_timeout_or_stop_request > start sem_timedwait from %d.%09lu to %d.%09lu \n",
+ SHOW("polling_thread > sleep_until_timeout_or_stop_request > start sem_timedwait from %d.%09lu to %d.%09lu \n",
to.tv_sec, to.tv_nsec,
ts.tv_sec, ts.tv_nsec);
- while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1
+ while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1
&& errno == EINTR)
{
continue; // Restart when interrupted by handler
}
assert (gettimeofday(&tv, NULL) != -1);
- SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n",
+ SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n",
tv.tv_sec, tv.tv_usec*1000);
if (err == 0)
@@ -421,7 +431,7 @@ ENTER("sleep_until_timeout_or_stop_request");
//>
//<get_remaining_time
// Asked for the time interval required for reaching the sample.
-// If the stream is opened but the audio samples are not played,
+// If the stream is opened but the audio samples are not played,
// a timeout is started.
static int get_remaining_time(uint32_t sample, uint32_t* time_in_ms, int* stop_is_required)
@@ -445,18 +455,18 @@ ENTER("get_remaining_time");
// stream opened but not active
//
- // Several possible states:
- // * the stream is opened but not yet started:
+ // Several possible states:
+ // * the stream is opened but not yet started:
//
// wait for the start of stream
//
- // * some samples have already been played,
+ // * some samples have already been played,
// ** the end of stream is reached
// ** or there is an underrun
- //
+ //
// wait for the close of stream
- *stop_is_required = sleep_until_timeout_or_stop_request( ACTIVITY_TIMEOUT);
+ *stop_is_required = sleep_until_timeout_or_stop_request( ACTIVITY_TIMEOUT);
}
return err;
@@ -496,8 +506,8 @@ ENTER("polling_thread");
SHOW_TIME("polling_thread > unlocked\n");
a_stop_is_required=0;
- a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
- if ((a_status==0) && a_stop_is_required)
+ a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); // NOTE: may set a_stop_is_required to -1
+ if ((a_status==0) && (a_stop_is_required > 0))
{
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while(0 == sem_trywait(&my_sem_stop_is_required))
@@ -510,27 +520,27 @@ ENTER("polling_thread");
}
// In this loop, my_event_is_running = 1
- while (head && !a_stop_is_required)
+ while (head && (a_stop_is_required <= 0))
{
SHOW_TIME("polling_thread > check head\n");
while(0 == sem_trywait(&my_sem_start_is_required))
{
};
-
+
espeak_EVENT* event = (espeak_EVENT*)(head->data);
assert(event);
-
+
uint32_t time_in_ms = 0;
-
- int err = get_remaining_time((uint32_t)event->sample,
- &time_in_ms,
+
+ int err = get_remaining_time((uint32_t)event->sample,
+ &time_in_ms,
&a_stop_is_required);
- if (a_stop_is_required)
+ if (a_stop_is_required > 0)
{
break;
}
else if (err != 0)
- {
+ {
// No available time: the event is deleted.
SHOW("polling_thread > %s\n","audio device down");
a_status = pthread_mutex_lock(&my_mutex);
@@ -544,22 +554,22 @@ ENTER("polling_thread");
if (my_callback)
{
event_notify(event);
- // the user_data (and the type) are cleaned to be sure
+ // the user_data (and the type) are cleaned to be sure
// that MSG_TERMINATED is called twice (at delete time too).
event->type=espeakEVENT_LIST_TERMINATED;
event->user_data=NULL;
}
-
+
a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked\n");
event_delete( (espeak_EVENT*)pop());
a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");
-
+
a_stop_is_required=0;
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
-
- if ((a_status==0) && a_stop_is_required)
+
+ if ((a_status==0) && (a_stop_is_required > 0))
{
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while(0 == sem_trywait(&my_sem_stop_is_required))
@@ -576,17 +586,17 @@ ENTER("polling_thread");
a_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms);
}
}
-
+
a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked\n");
-
+
SHOW_TIME("polling_thread > my_event_is_running = 0\n");
my_event_is_running = 0;
-
- if(!a_stop_is_required)
+
+ if(a_stop_is_required <= 0)
{
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
- if ((a_status==0) && a_stop_is_required)
+ if ((a_status==0) && (a_stop_is_required > 0))
{
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while(0 == sem_trywait(&my_sem_stop_is_required))
@@ -602,8 +612,8 @@ ENTER("polling_thread");
a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");
- if (a_stop_is_required)
- {
+ if (a_stop_is_required > 0)
+ {
SHOW("polling_thread > %s\n","stop required!");
// no mutex required since the stop command is synchronous
// and waiting for my_sem_stop_is_acknowledged
@@ -639,13 +649,13 @@ static espeak_ERROR push(void* the_data)
SHOW("event > push > %s\n", "EE_BUFFER_FULL");
return EE_BUFFER_FULL;
}
-
+
node *n = (node *)malloc(sizeof(node));
if (n == NULL)
{
return EE_INTERNAL_ERROR;
}
-
+
if (head == NULL)
{
head = n;
@@ -656,13 +666,13 @@ static espeak_ERROR push(void* the_data)
tail->next = n;
tail = n;
}
-
+
tail->next = NULL;
tail->data = the_data;
-
+
node_counter++;
SHOW("event > push > counter=%d (uid=%d)\n",node_counter,((espeak_EVENT*)the_data)->unique_identifier);
-
+
return EE_OK;
}
@@ -670,9 +680,9 @@ static void* pop()
{
ENTER("event > pop");
void* the_data = NULL;
-
+
assert((!head && !tail) || (head && tail));
-
+
if (head != NULL)
{
node* n = head;
@@ -682,12 +692,12 @@ static void* pop()
node_counter--;
SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n",the_data, node_counter,((espeak_EVENT*)the_data)->unique_identifier);
}
-
+
if(head == NULL)
{
tail = NULL;
}
-
+
return the_data;
}
@@ -695,10 +705,10 @@ static void* pop()
static void init()
{
ENTER("event > init");
-
+
while (event_delete( (espeak_EVENT*)pop() ))
{}
-
+
node_counter = 0;
}
@@ -707,8 +717,8 @@ static void init()
void event_terminate()
{
ENTER("event_terminate");
-
- if (my_thread)
+
+ if (thread_inited)
{
pthread_cancel(my_thread);
pthread_join(my_thread,NULL);
@@ -717,6 +727,7 @@ ENTER("event_terminate");
sem_destroy(&my_sem_stop_is_required);
sem_destroy(&my_sem_stop_is_acknowledged);
init(); // purge event
+ thread_inited = 0;
}
}
diff --git a/navit/support/espeak/event.h b/navit/support/espeak/event.h
index c9ee482b0..b158d5eb3 100644
--- a/navit/support/espeak/event.h
+++ b/navit/support/espeak/event.h
@@ -34,7 +34,7 @@ void event_set_callback(t_espeak_callback* cb);
//
// Return: EE_OK: operation achieved
// EE_INTERNAL_ERROR.
-espeak_ERROR event_clear_all ();
+espeak_ERROR event_clear_all (void);
// Declare a future event
//
@@ -46,6 +46,6 @@ espeak_ERROR event_declare (espeak_EVENT* event);
// Terminate the event component.
// Last function to be called.
-void event_terminate();
+void event_terminate(void);
#endif
diff --git a/navit/support/espeak/fifo.c b/navit/support/espeak/fifo.c
index 95d5ab1ed..97c0fb9cc 100644
--- a/navit/support/espeak/fifo.c
+++ b/navit/support/espeak/fifo.c
@@ -24,7 +24,9 @@
//<includes
+#ifndef PLATFORM_WINDOWS
#include <unistd.h>
+#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
@@ -43,7 +45,7 @@
//>
//<decls and function prototypes
-// my_mutex: protects my_thread_is_talking,
+// my_mutex: protects my_thread_is_talking,
// my_stop_is_required, and the command fifo
static pthread_mutex_t my_mutex;
static int my_command_is_running = 0;
@@ -51,7 +53,7 @@ static int my_stop_is_required = 0;
// + fifo
//
-// my_thread: reads commands from the fifo, and runs them.
+// my_thread: reads commands from the fifo, and runs them.
static pthread_t my_thread;
static sem_t my_sem_start_is_required;
static sem_t my_sem_stop_is_acknowledged;
@@ -60,7 +62,7 @@ static void* say_thread(void*);
static espeak_ERROR push(t_espeak_command* the_command);
static t_espeak_command* pop();
-static void init();
+static void init(int process_parameters);
static int node_counter=0;
enum {MAX_NODE_COUNTER=400,
INACTIVITY_TIMEOUT=50, // in ms, check that the stream is inactive
@@ -75,17 +77,17 @@ void fifo_init()
// security
pthread_mutex_init( &my_mutex, (const pthread_mutexattr_t *)NULL);
- init();
+ init(0);
assert(-1 != sem_init(&my_sem_start_is_required, 0, 0));
assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0));
- pthread_attr_t a_attrib;
+ pthread_attr_t a_attrib;
if (pthread_attr_init (& a_attrib)
|| pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE)
- || pthread_create( &my_thread,
- & a_attrib,
- say_thread,
+ || pthread_create( &my_thread,
+ & a_attrib,
+ say_thread,
(void*)NULL))
{
assert(0);
@@ -107,8 +109,8 @@ void fifo_init()
espeak_ERROR fifo_add_command (t_espeak_command* the_command)
{
ENTER("fifo_add_command");
-
- int a_status = pthread_mutex_lock(&my_mutex);
+
+ int a_status = pthread_mutex_lock(&my_mutex);
espeak_ERROR a_error = EE_OK;
if (!a_status)
@@ -121,12 +123,12 @@ espeak_ERROR fifo_add_command (t_espeak_command* the_command)
if (!a_status && !my_command_is_running && (a_error == EE_OK))
{
- // quit when command is actually started
+ // quit when command is actually started
// (for possible forthcoming 'end of command' checks)
SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required);
int val=1;
- while (val)
+ while (val > 0)
{
usleep(50000); // TBD: event?
sem_getvalue(&my_sem_start_is_required, &val);
@@ -148,8 +150,8 @@ espeak_ERROR fifo_add_command (t_espeak_command* the_command)
espeak_ERROR fifo_add_commands (t_espeak_command* command1, t_espeak_command* command2)
{
ENTER("fifo_add_command");
-
- int a_status = pthread_mutex_lock(&my_mutex);
+
+ int a_status = pthread_mutex_lock(&my_mutex);
espeak_ERROR a_error = EE_OK;
if (!a_status)
@@ -172,12 +174,12 @@ espeak_ERROR fifo_add_commands (t_espeak_command* command1, t_espeak_command* co
if (!a_status && !my_command_is_running && (a_error == EE_OK))
{
- // quit when one command is actually started
+ // quit when one command is actually started
// (for possible forthcoming 'end of command' checks)
SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required);
int val=1;
- while (val)
+ while (val > 0)
{
usleep(50000); // TBD: event?
sem_getvalue(&my_sem_start_is_required, &val);
@@ -275,54 +277,58 @@ static int sleep_until_start_request_or_inactivity()
int a_start_is_required=0;
// Wait for the start request (my_sem_start_is_required).
- // Besides this, if the audio stream is still busy,
- // check from time to time its end.
- // The end of the stream is confirmed by several checks
+ // Besides this, if the audio stream is still busy,
+ // check from time to time its end.
+ // The end of the stream is confirmed by several checks
// for filtering underflow.
//
int i=0;
- while((i<= MAX_INACTIVITY_CHECK) && !a_start_is_required)
- {
- if (wave_is_busy( NULL) )
+ while((i<= MAX_INACTIVITY_CHECK) && !a_start_is_required)
{
- i = 0;
- }
+ if (wave_is_busy( NULL) )
+ {
+ i = 0;
+ }
else
- {
- i++;
- }
+ {
+ i++;
+ }
- int err=0;
- struct timespec ts, to;
- struct timeval tv;
-
- clock_gettime2( &ts);
- to.tv_sec = ts.tv_sec;
- to.tv_nsec = ts.tv_nsec;
-
- add_time_in_ms( &ts, INACTIVITY_TIMEOUT);
-
- SHOW("fifo > sleep_until_start_request_or_inactivity > start sem_timedwait (start_is_required) from %d.%09lu to %d.%09lu \n",
- to.tv_sec, to.tv_nsec,
- ts.tv_sec, ts.tv_nsec);
-
- while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1
- && errno == EINTR)
- {
- continue;
- }
-
- assert (gettimeofday(&tv, NULL) != -1);
- SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err,
- tv.tv_sec, tv.tv_usec*1000);
-
- if (err==0)
- {
- a_start_is_required = 1;
+ int err=0;
+ struct timespec ts;
+ struct timeval tv;
+
+ clock_gettime2( &ts);
+
+#ifdef DEBUG_ENABLED
+ struct timespec to;
+ to.tv_sec = ts.tv_sec;
+ to.tv_nsec = ts.tv_nsec;
+#endif
+
+ add_time_in_ms( &ts, INACTIVITY_TIMEOUT);
+
+ SHOW("fifo > sleep_until_start_request_or_inactivity > start sem_timedwait (start_is_required) from %d.%09lu to %d.%09lu \n",
+ to.tv_sec, to.tv_nsec,
+ ts.tv_sec, ts.tv_nsec);
+
+ while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1
+ && errno == EINTR)
+ {
+ continue;
+ }
+
+ assert (gettimeofday(&tv, NULL) != -1);
+ SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err,
+ tv.tv_sec, tv.tv_usec*1000);
+
+ if (err==0)
+ {
+ a_start_is_required = 1;
+ }
}
- }
- SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > LEAVE");
- return a_start_is_required;
+ SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > LEAVE");
+ return a_start_is_required;
}
//>
@@ -332,8 +338,8 @@ static void close_stream()
{
SHOW_TIME("fifo > close_stream > ENTER\n");
- // Warning: a wave_close can be already required by
- // an external command (espeak_Cancel + fifo_stop), if so:
+ // Warning: a wave_close can be already required by
+ // an external command (espeak_Cancel + fifo_stop), if so:
// my_stop_is_required = 1;
int a_status = pthread_mutex_lock(&my_mutex);
@@ -355,7 +361,7 @@ static void close_stream()
a_stop_is_required = my_stop_is_required;
a_status = pthread_mutex_unlock(&my_mutex);
-
+
if (a_stop_is_required)
{
// acknowledge the stop request
@@ -434,7 +440,7 @@ static void* say_thread(void*)
};
if (my_stop_is_required)
- {
+ {
SHOW_TIME("say_thread > my_command_is_running = 0\n");
my_command_is_running = 0;
}
@@ -450,26 +456,26 @@ static void* say_thread(void*)
}
if (my_stop_is_required)
- {
+ {
// no mutex required since the stop command is synchronous
// and waiting for my_sem_stop_is_acknowledged
- init();
+ init(1);
// purge start semaphore
SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
while(0==sem_trywait(&my_sem_start_is_required))
{
};
-
+
// acknowledge the stop request
SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n");
int a_status = sem_post(&my_sem_stop_is_acknowledged);
assert( a_status != -1);
}
// and wait for the next start
- SHOW_TIME("say_thread > wait for my_sem_start_is_required\n");
+ SHOW_TIME("say_thread > wait for my_sem_start_is_required\n");
}
-
+
return NULL;
}
@@ -513,7 +519,7 @@ static espeak_ERROR push(t_espeak_command* the_command)
{
return EE_INTERNAL_ERROR;
}
-
+
if (head == NULL)
{
head = n;
@@ -524,7 +530,7 @@ static espeak_ERROR push(t_espeak_command* the_command)
tail->next = n;
tail = n;
}
-
+
tail->next = NULL;
tail->data = the_command;
@@ -560,19 +566,29 @@ static t_espeak_command* pop()
}
display_espeak_command(the_command);
-
+
return the_command;
}
-static void init()
+static void init(int process_parameters)
{
- ENTER("fifo > init");
- while (delete_espeak_command( pop() ))
- {}
- node_counter = 0;
+ // Changed by Tyler Spivey 30.Nov.2011
+ t_espeak_command *c = NULL;
+ ENTER("fifo > init");
+ c = pop();
+ while (c != NULL) {
+ if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC))
+ {
+ process_espeak_command(c);
+ }
+ delete_espeak_command(c);
+ c = pop();
+ }
+ node_counter = 0;
}
+
//>
//<fifo_init
void fifo_terminate()
@@ -585,7 +601,7 @@ void fifo_terminate()
sem_destroy(&my_sem_start_is_required);
sem_destroy(&my_sem_stop_is_acknowledged);
- init(); // purge fifo
+ init(0); // purge fifo
}
#endif
diff --git a/navit/support/espeak/fifo.h b/navit/support/espeak/fifo.h
index b3699ff9b..0077d5db6 100644
--- a/navit/support/espeak/fifo.h
+++ b/navit/support/espeak/fifo.h
@@ -9,7 +9,7 @@
// Initialize the fifo component.
// First function to be called.
-void fifo_init();
+void fifo_init(void);
// Add an espeak command.
//
@@ -36,15 +36,15 @@ espeak_ERROR fifo_add_commands (t_espeak_command* c1, t_espeak_command* c2);
// The current running command must be stopped and the awaiting commands are cleared.
// Return: EE_OK: operation achieved
// EE_INTERNAL_ERROR.
-espeak_ERROR fifo_stop ();
+espeak_ERROR fifo_stop (void);
// Is there a running command?
// Returns 1 if yes; 0 otherwise.
-int fifo_is_busy ();
+int fifo_is_busy (void);
// Terminate the fifo component.
// Last function to be called.
-void fifo_terminate();
+void fifo_terminate(void);
// Indicates if the running command is still enabled.
//
@@ -53,6 +53,6 @@ void fifo_terminate();
// stopping speech as soon as a cancel command is applied.
//
// Returns 1 if yes, or 0 otherwise.
-int fifo_is_command_enabled();
+int fifo_is_command_enabled(void);
#endif
diff --git a/navit/support/espeak/intonation.c b/navit/support/espeak/intonation.c
index 61b2ff282..04d72a9c8 100755..100644
--- a/navit/support/espeak/intonation.c
+++ b/navit/support/espeak/intonation.c
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
#include <wctype.h>
#include "speak_lib.h"
@@ -45,8 +46,8 @@ typedef struct {
char env;
char flags; //bit 0=pitch rising, bit1=emnphasized, bit2=end of clause
char nextph_type;
- short pitch1;
- short pitch2;
+ unsigned char pitch1;
+ unsigned char pitch2;
} SYLLABLE;
static SYLLABLE *syllable_tab;
@@ -61,10 +62,10 @@ static int tone_pitch_env; /* used to return pitch envelope */
#define PITCHfall 0
-#define PITCHrise 1
-#define PITCHfrise 2 // and 3 must be for the varient preceded by 'r'
-#define PITCHfrise2 4 // and 5 must be the 'r' variant
-#define PITCHrisefall 6
+#define PITCHrise 2
+#define PITCHfrise 4 // and 3 must be for the variant preceded by 'r'
+#define PITCHfrise2 6 // and 5 must be the 'r' variant
+#define PITCHrisefall 8
/* 0 fall */
unsigned char env_fall[128] = {
@@ -191,9 +192,9 @@ static unsigned char env_risefallrise[128] = {
-unsigned char *envelope_data[18] = {
- env_fall,
- env_rise,
+unsigned char *envelope_data[N_ENVELOPE_DATA] = {
+ env_fall, env_fall,
+ env_rise, env_rise,
env_frise, env_r_frise,
env_frise2, env_r_frise2,
env_risefall, env_risefall,
@@ -206,18 +207,17 @@ unsigned char *envelope_data[18] = {
};
-/* all pitches given in Hz above pitch_base */
+
+/* indexed by stress */
+static int min_drop[] = {6,7,9,9,20,20,20,25};
// pitch change during the main part of the clause
-static int drops_0[8] = {0x400,0x400,0x700,0x700,0x700,0xa00,0x1800,0x0e00};
-//static int drops_1[8] = {0x400,0x400,0x600,0x600,0xc00,0xc00,0x0e00,0x0e00};
-//static int drops_2[8] = {0x400,0x400,0x600,0x600,-0x800,0xc00,0x0e00,0x0e00};
+static int drops_0[8] = {9,9,16,16,16,23,55,32};
-static short oflow[] = {0, 20, 12, 4, 0};
-static short oflow_emf[] = {5, 24, 15, 10, 5};
-static short oflow_less[] = {3, 19, 12, 7, 2};
-// static short oflow_test2[] = {20, 0, 20, 0, 20};
-// static short back_emf[] = {35, 32, 0};
+// overflow table values are 64ths of the body pitch range (between body_start and body_end)
+static signed char oflow[] = {0, 40, 24, 8, 0};
+static signed char oflow_emf[] = {10, 52, 32, 20, 10};
+static signed char oflow_less[] = {6, 38, 24, 14, 4};
#define N_TONE_HEAD_TABLE 13
@@ -235,8 +235,8 @@ typedef struct {
unsigned char body_max_steps;
char body_lower_u;
- char n_overflow;
- short *overflow;
+ unsigned char n_overflow;
+ signed char *overflow;
} TONE_HEAD;
@@ -259,38 +259,38 @@ typedef struct {
#define T_EMPH 1
static TONE_HEAD tone_head_table[N_TONE_HEAD_TABLE] = {
- {20, 25, 34, 22, drops_0, 3, 3, 5, oflow}, // 0 statement
- {20, 25, 34, 20, drops_0, 3, 3, 5, oflow}, // 1 comma
- {20, 25, 34, 20, drops_0, 3, 3, 5, oflow}, // 2 question
- {20, 25, 36, 22, drops_0, 3, 4, 5, oflow_emf}, // 3 exclamation
- {20, 25, 34, 22, drops_0, 3, 3, 5, oflow}, // 4 statement, emphatic
- {20, 25, 32, 24, drops_0, 4, 3, 5, oflow_less}, // 5 statement, less intonation
- {20, 25, 32, 24, drops_0, 4, 3, 5, oflow_less}, // 6 comma, less intonation
- {20, 25, 32, 24, drops_0, 4, 3, 5, oflow_less}, // 7 comma, less intonation, less rise
- {20, 25, 34, 22, drops_0, 3, 3, 5, oflow}, // 8 pitch raises at end of sentence
- {20, 25, 34, 20, drops_0, 3, 3, 5, oflow}, // 9 comma
- {20, 25, 34, 22, drops_0, 3, 3, 5, oflow}, // 10 question
- {15, 18, 18, 14, drops_0, 3, 3, 5, oflow_less}, // 11 test
- {20, 25, 24, 22, drops_0, 3, 3, 5, oflow_less}, // 12 test
+ {46, 57, 78, 50, drops_0, 3, 7, 5, oflow}, // 0 statement
+ {46, 57, 78, 46, drops_0, 3, 7, 5, oflow}, // 1 comma
+ {46, 57, 78, 46, drops_0, 3, 7, 5, oflow}, // 2 question
+ {46, 57, 90, 50, drops_0, 3, 9, 5, oflow_emf}, // 3 exclamation
+ {46, 57, 78, 50, drops_0, 3, 7, 5, oflow}, // 4 statement, emphatic
+ {46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less}, // 5 statement, less intonation
+ {46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less}, // 6 comma, less intonation
+ {46, 57, 74, 55, drops_0, 4, 7, 5, oflow_less}, // 7 comma, less intonation, less rise
+ {46, 57, 78, 50, drops_0, 3, 7, 5, oflow}, // 8 pitch raises at end of sentence
+ {46, 57, 78, 46, drops_0, 3, 7, 5, oflow}, // 9 comma
+ {46, 57, 78, 50, drops_0, 3, 7, 5, oflow}, // 10 question
+ {34, 41, 41, 32, drops_0, 3, 7, 5, oflow_less}, // 11 test
+ {46, 57, 55, 50, drops_0, 3, 7, 5, oflow_less}, // 12 test
};
static TONE_NUCLEUS tone_nucleus_table[N_TONE_NUCLEUS_TABLE] = {
- {PITCHfall, 30, 5, PITCHfall, 32, 9, NULL, 12, 7, 0}, // 0 statement
- {PITCHfrise, 35, 8, PITCHfrise2, 35,10, NULL, 15, 23, 0}, // 1 comma
- {PITCHfrise, 39,10, PITCHfrise2, 36,10, NULL, 15, 28, 0}, // 2 question
-// {PITCHfall, 41, 4, PITCHfall, 41,27, NULL, 16, 4, T_EMPH}, // 3 exclamation
- {PITCHfall, 41, 4, PITCHfall, 41,35, NULL, 35, 4, T_EMPH}, // 3 exclamation
- {PITCHfall, 38, 2, PITCHfall, 42,30, NULL, 15, 5, 0}, // 4 statement, emphatic
- {PITCHfall, 28, 5, PITCHfall, 28, 9, NULL, 12, 7, 0}, // 5 statement, less intonation
- {PITCHfrise, 30, 8, PITCHfrise2, 30,10, NULL, 13, 20, 0}, // 6 comma, less intonation
- {PITCHfrise2, 28, 7, PITCHfall, 29,14, NULL, 14, 8, 0}, // 7 comma, less intonation, less rise
- {PITCHrise, 30,20, PITCHfall, 19,14, NULL, 20, 26, 0}, // 8 pitch raises at end of sentence
- {PITCHfrise, 35,11, PITCHfrise2, 32,10, NULL, 19, 24, 0}, // 9 comma
- {PITCHfrise, 39,15, PITCHfall, 28,14, NULL, 20, 36, 0}, // 10 question
- {PITCHfall, 28, 6, PITCHfall, 28,10, NULL, 12, 6, 0}, // 11 test
- {PITCHfall, 35, 9, PITCHfall, 35,12, NULL, 16, 10, 0}, // 12 test
+ {PITCHfall, 64, 8, PITCHfall, 70,18, NULL, 24, 12, 0}, // 0 statement
+ {PITCHfrise, 80,18, PITCHfrise2, 78,22, NULL, 34, 52, 0}, // 1 comma
+ {PITCHfrise, 88,22, PITCHfrise2, 82,22, NULL, 34, 64, 0}, // 2 question
+ {PITCHfall, 92, 8, PITCHfall, 92,80, NULL, 76, 8, T_EMPH}, // 3 exclamation
+
+ {PITCHfall, 86, 4, PITCHfall, 94,66, NULL, 34, 10, 0}, // 4 statement, emphatic
+ {PITCHfall, 62,10, PITCHfall, 62,20, NULL, 28, 16, 0}, // 5 statement, less intonation
+ {PITCHfrise, 68,18, PITCHfrise2, 68,22, NULL, 30, 44, 0}, // 6 comma, less intonation
+ {PITCHfrise2, 64,16, PITCHfall, 66,32, NULL, 32, 18, 0}, // 7 comma, less intonation, less rise
+ {PITCHrise, 68,46, PITCHfall, 42,32, NULL, 46, 58, 0}, // 8 pitch raises at end of sentence
+ {PITCHfrise, 78,24, PITCHfrise2, 72,22, NULL, 42, 52, 0}, // 9 comma
+ {PITCHfrise, 88,34, PITCHfall, 64,32, NULL, 46, 82, 0}, // 10 question
+ {PITCHfall, 56,12, PITCHfall, 56,20, NULL, 24, 12, 0}, // 11 test
+ {PITCHfall, 70,18, PITCHfall, 70,24, NULL, 32, 20, 0}, // 12 test
};
-
+
/* index by 0=. 1=, 2=?, 3=! 4=none, 5=emphasized */
@@ -306,11 +306,8 @@ unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS] = {
};
-
-/* indexed by stress */
-static int min_drop[] = {0x300,0x300,0x400,0x400,0x900,0x900,0x900,0xb00};
-
-
+int n_tunes = 0;
+TUNE *tunes = NULL;
#define SECONDARY 3
@@ -341,7 +338,7 @@ static void count_pitch_vowels(int start, int end, int clause_end)
number_body = 0;
number_tail = 0; /* number between tonic syllable and next primary */
last_primary = -1;
-
+
for(ix=start; ix<end; ix++)
{
stress = syllable_tab[ix].stress; /* marked stress level */
@@ -419,20 +416,17 @@ static int count_increments(int ix, int end_ix, int min_stress)
+
static void set_pitch(SYLLABLE *syl, int base, int drop)
/******************************************************/
-// Set the pitch of a vowel in syllable_tab. Base & drop are Hz * 256
+// Set the pitch of a vowel in syllable_tab
{
int pitch1, pitch2;
int flags = 0;
- /* adjust experimentally */
- int pitch_range2 = 148;
- int pitch_base2 = 72;
-
if(base < 0) base = 0;
- pitch2 = ((base * pitch_range2 ) >> 15) + pitch_base2;
+ pitch2 = base;
if(drop < 0)
{
@@ -440,10 +434,12 @@ static void set_pitch(SYLLABLE *syl, int base, int drop)
drop = -drop;
}
- pitch1 = pitch2 + ((drop * pitch_range2) >> 15);
+ pitch1 = pitch2 + drop;
+ if(pitch1 < 0)
+ pitch1 = 0;
- if(pitch1 > 511) pitch1 = 511;
- if(pitch2 > 511) pitch2 = 511;
+ if(pitch1 > 254) pitch1 = 254;
+ if(pitch2 > 254) pitch2 = 254;
syl->pitch1 = pitch1;
syl->pitch2 = pitch2;
@@ -451,6 +447,164 @@ static void set_pitch(SYLLABLE *syl, int base, int drop)
} /* end of set_pitch */
+static int CountUnstressed(int start, int end, int limit)
+{//======================================================
+ int ix;
+
+ for(ix=start; ix <= end; ix++)
+ {
+ if(syllable_tab[ix].stress >= limit)
+ break;
+ }
+ return(ix - start);
+}
+
+static int SetHeadIntonation(TUNE *tune, int syl_ix, int end_ix, int control)
+{//==========================================================================
+ int stress;
+ SYLLABLE *syl;
+ int ix;
+ int pitch=0;
+ int increment=0;
+ int n_steps=0;
+ int stage; // onset, head, last
+ int initial;
+ int overflow_ix=0;
+ int pitch_range;
+ int pitch_range_abs;
+ int *drops;
+ int n_unstressed=0;
+ int unstressed_ix=0;
+ int unstressed_inc;
+ int used_onset = 0;
+ int head_final = end_ix;
+int secondary=2; // 2
+
+ pitch_range = (tune->head_end - tune->head_start) << 8;
+ pitch_range_abs = abs(pitch_range);
+ drops = drops_0; // this should be controled by tune->head_drops
+ initial = 1;
+
+ stage = 0;
+ if(tune->onset == 255)
+ stage = 1; // no onset specified
+
+ if(tune->head_last != 255)
+ {
+ // find the last primary stress in the body
+ for(ix = end_ix-1; ix >= syl_ix; ix--)
+ {
+ if(syllable_tab[ix].stress >= 4)
+ {
+ head_final = ix;
+ break;
+ }
+ }
+ }
+
+ while(syl_ix < end_ix)
+ {
+ syl = &syllable_tab[syl_ix];
+ stress = syl->stress;
+
+ if(initial || (stress >= 4))
+ {
+ // a primary stress
+
+ if((initial) || (stress == 5))
+ {
+ initial = 0;
+ overflow_ix = 0;
+
+ if(tune->onset == 255)
+ {
+ n_steps = count_increments(syl_ix, head_final, 4);
+ pitch = tune->head_start << 8;
+ }
+ else
+ {
+ // a pitch has been specified for the onset syllable, don't include it in the pitch incrementing
+ n_steps = count_increments(syl_ix+1, head_final, 4);
+ pitch = tune->onset << 8;
+ used_onset = 1;
+ }
+
+ if(n_steps > tune->head_max_steps)
+ n_steps = tune->head_max_steps;
+
+ if(n_steps > 1)
+ {
+ increment = pitch_range / (n_steps -1);
+ }
+ else
+ increment = 0;
+
+ }
+ else
+ if(syl_ix == head_final)
+ {
+ // a pitch has been specified for the last primary stress before the nucleus
+ pitch = tune->head_last << 8;
+ stage = 2;
+ }
+ else
+ {
+ if(used_onset)
+ {
+ stage = 1;
+ used_onset = 0;
+ pitch = tune->head_start << 8;
+ n_steps++;
+ }
+ else
+ if(n_steps > 0)
+ pitch += increment;
+ else
+ {
+ pitch = (tune->head_end << 8) + (pitch_range_abs * tune->head_extend[overflow_ix++])/64;
+ if(overflow_ix >= tune->n_head_extend)
+ {
+ overflow_ix = 0;
+ }
+ }
+ }
+
+ n_steps--;
+ }
+
+ if(stress >= PRIMARY)
+ {
+ n_unstressed = CountUnstressed(syl_ix+1, end_ix, secondary);
+ unstressed_ix = 0;
+ syl->stress = PRIMARY_STRESSED;
+ syl->env = tune->stressed_env;
+ set_pitch(syl,(pitch >> 8), tune->stressed_drop);
+ }
+ else
+ if(stress >= secondary)
+ {
+ n_unstressed = CountUnstressed(syl_ix+1, end_ix, secondary);
+ unstressed_ix = 0;
+ set_pitch(syl,(pitch >> 8),drops[stress]);
+ }
+ else
+ {
+ if(n_unstressed > 1)
+ unstressed_inc = (tune->unstr_end[stage] - tune->unstr_start[stage]) / (n_unstressed - 1);
+ else
+ unstressed_inc = 0;
+
+ set_pitch(syl, (pitch >> 8) + tune->unstr_start[stage] + (unstressed_inc * unstressed_ix), drops[stress]);
+ unstressed_ix++;
+ }
+
+ syl_ix++;
+ }
+ return(syl_ix);
+
+} // end of SetBodyIntonation
+
+
static int calc_pitch_segment(int ix, int end_ix, TONE_HEAD *th, TONE_NUCLEUS *tn, int min_stress, int continuing)
/**********************************************************************************************/
@@ -466,13 +620,17 @@ static int calc_pitch_segment(int ix, int end_ix, TONE_HEAD *th, TONE_NUCLEUS *t
int initial;
int overflow=0;
int n_overflow;
+ int pitch_range;
+ int pitch_range_abs;
int *drops;
- short *overflow_tab;
+ signed char *overflow_tab;
SYLLABLE *syl;
- static short continue_tab[5] = {-13, 16, 10, 4, 0};
+ static signed char continue_tab[5] = {-26, 32, 20, 8, 0};
drops = th->body_drops;
+ pitch_range = (th->body_end - th->body_start) << 8;
+ pitch_range_abs = abs(pitch_range);
if(continuing)
{
@@ -480,8 +638,7 @@ static int calc_pitch_segment(int ix, int end_ix, TONE_HEAD *th, TONE_NUCLEUS *t
overflow = 0;
n_overflow = 5;
overflow_tab = continue_tab;
- increment = (th->body_end - th->body_start) << 8;
- increment = increment / (th->body_max_steps -1);
+ increment = pitch_range / (th->body_max_steps -1);
}
else
{
@@ -513,8 +670,7 @@ static int calc_pitch_segment(int ix, int end_ix, TONE_HEAD *th, TONE_NUCLEUS *t
if(n_steps > 1)
{
- increment = (th->body_end - th->body_start) << 8;
- increment = increment / (n_steps -1);
+ increment = pitch_range / (n_steps -1);
}
else
increment = 0;
@@ -527,7 +683,7 @@ static int calc_pitch_segment(int ix, int end_ix, TONE_HEAD *th, TONE_NUCLEUS *t
pitch += increment;
else
{
- pitch = (th->body_end << 8) - (increment * overflow_tab[overflow++])/16;
+ pitch = (th->body_end << 8) + (pitch_range_abs * overflow_tab[overflow++])/64;
if(overflow >= n_overflow)
{
overflow = 0;
@@ -541,27 +697,27 @@ static int calc_pitch_segment(int ix, int end_ix, TONE_HEAD *th, TONE_NUCLEUS *t
n_primary--;
if((tn->backwards) && (n_primary < 2))
{
- pitch = tn->backwards[n_primary] << 8;
+ pitch = tn->backwards[n_primary] << 8;
}
}
if(stress >= PRIMARY)
{
syl->stress = PRIMARY_STRESSED;
- set_pitch(syl,pitch,drops[stress]);
+ set_pitch(syl,(pitch >> 8),drops[stress]);
}
else
if(stress >= SECONDARY)
{
- set_pitch(syl,pitch,drops[stress]);
+ set_pitch(syl,(pitch >> 8),drops[stress]);
}
else
{
/* unstressed, drop pitch if preceded by PRIMARY */
if((syllable_tab[ix-1].stress & 0x3f) >= SECONDARY)
- set_pitch(syl,pitch - (th->body_lower_u << 8), drops[stress]);
+ set_pitch(syl,(pitch >> 8) - th->body_lower_u, drops[stress]);
else
- set_pitch(syl,pitch,drops[stress]);
+ set_pitch(syl,(pitch >> 8),drops[stress]);
}
ix++;
@@ -571,13 +727,12 @@ static int calc_pitch_segment(int ix, int end_ix, TONE_HEAD *th, TONE_NUCLEUS *t
+static void SetPitchGradient(int start_ix, int end_ix, int start_pitch, int end_pitch)
+{//====================================================================================
+// Set a linear pitch change over a number of syllables.
+// Used for pre-head, unstressed syllables in the body, and the tail
-
-static int calc_pitch_segment2(int ix, int end_ix, int start_p, int end_p, int min_stress)
-/****************************************************************************************/
-/* Linear pitch rise/fall, change pitch at min_stress or stronger
- Used for pre-head and tail */
-{
+ int ix;
int stress;
int pitch;
int increment;
@@ -585,54 +740,112 @@ static int calc_pitch_segment2(int ix, int end_ix, int start_p, int end_p, int m
int drop;
SYLLABLE *syl;
- if(ix >= end_ix)
- return(ix);
-
- n_increments = count_increments(ix,end_ix,min_stress);
- increment = (end_p - start_p) << 8;
-
+ increment = (end_pitch - start_pitch) << 8;
+ n_increments = end_ix - start_ix;
+
+ if(n_increments <= 0)
+ return;
+
if(n_increments > 1)
{
increment = increment / n_increments;
}
-
- pitch = start_p << 8;
- while(ix < end_ix)
+ pitch = start_pitch << 8;
+
+ for(ix=start_ix; ix < end_ix; ix++)
{
syl = &syllable_tab[ix];
stress = syl->stress;
if(increment > 0)
{
- set_pitch(syl,pitch,-increment);
+ set_pitch(syl,(pitch >> 8),-(increment >> 8));
pitch += increment;
}
else
{
- drop = -increment;
+ drop = -(increment >> 8);
if(drop < min_drop[stress])
drop = min_drop[stress];
pitch += increment;
- if(drop > 0x900)
- drop = 0x900;
- set_pitch(syl, pitch, drop);
+ if(drop > 18)
+ drop = 18;
+ set_pitch(syl, (pitch >> 8), drop);
}
-
- ix++;
}
- return(ix);
-} /* end of calc_pitch_segment2 */
+} // end of SetPitchGradient
+static int calc_pitches2(int start, int end, int tune_number)
+//============================================================
+// Calculate pitch values for the vowels in this tone group
+{
+ int ix;
+ TUNE *tune;
+ int drop;
+ tune = &tunes[tune_number];
+ ix = start;
+ /* vowels before the first primary stress */
+ /******************************************/
+
+ SetPitchGradient(ix, ix+number_pre, tune->prehead_start, tune->prehead_end);
+ ix += number_pre;
+
+ /* body of tonic segment */
+ /*************************/
-static int calc_pitches(int start, int end, int head_tone, int nucleus_tone)
-//===========================================================================
+ if(option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE)
+ {
+ tone_posn = tone_posn2; // put tone on the penultimate stressed word
+ }
+ ix = SetHeadIntonation(tune, ix, tone_posn, 0);
+
+ if(no_tonic)
+ return(0);
+
+ /* tonic syllable */
+ /******************/
+
+// if(tn->flags & T_EMPH)
+// {
+// syllable_tab[ix].flags |= SYL_EMPHASIS;
+// }
+
+ if(number_tail == 0)
+ {
+ tone_pitch_env = tune->nucleus0_env;
+ drop = tune->nucleus0_max - tune->nucleus0_min;
+ set_pitch(&syllable_tab[ix++],tune->nucleus0_min, drop);
+ }
+ else
+ {
+ tone_pitch_env = tune->nucleus1_env;
+ drop = tune->nucleus1_max - tune->nucleus1_min;
+ set_pitch(&syllable_tab[ix++],tune->nucleus1_min, drop);
+ }
+
+ syllable_tab[tone_posn].env = tone_pitch_env;
+ if(syllable_tab[tone_posn].stress == PRIMARY)
+ syllable_tab[tone_posn].stress = PRIMARY_STRESSED;
+
+ /* tail, after the tonic syllable */
+ /**********************************/
+
+ SetPitchGradient(ix, end, tune->tail_start, tune->tail_end);
+
+ return(tone_pitch_env);
+} /* end of calc_pitches2 */
+
+
+
+static int calc_pitches(int control, int start, int end, int tune_number)
+//========================================================================
// Calculate pitch values for the vowels in this tone group
{
int ix;
@@ -641,36 +854,40 @@ static int calc_pitches(int start, int end, int head_tone, int nucleus_tone)
int drop;
int continuing = 0;
+ if(control == 0)
+ {
+ return(calc_pitches2(start, end, tune_number));
+ }
+
if(start > 0)
continuing = 1;
- th = &tone_head_table[head_tone];
- tn = &tone_nucleus_table[nucleus_tone];
+ th = &tone_head_table[tune_number];
+ tn = &tone_nucleus_table[tune_number];
ix = start;
/* vowels before the first primary stress */
/******************************************/
- if(number_pre > 0)
- {
- ix = calc_pitch_segment2(ix, ix+number_pre, th->pre_start, th->pre_end, 0);
- }
+ SetPitchGradient(ix, ix+number_pre, th->pre_start, th->pre_end);
+ ix += number_pre;
/* body of tonic segment */
/*************************/
if(option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE)
{
- tone_posn = tone_posn2; // put tone on the penultimate stressed word
+ tone_posn = tone_posn2; // put tone on the penultimate stressed word
}
ix = calc_pitch_segment(ix,tone_posn, th, tn, PRIMARY, continuing);
-
+// ix = SetBodyIntonation(&tunes[0], ix, tone_posn, 0);
+
if(no_tonic)
return(0);
/* tonic syllable */
/******************/
-
+
if(tn->flags & T_EMPH)
{
syllable_tab[ix].flags |= SYL_EMPHASIS;
@@ -680,13 +897,13 @@ static int calc_pitches(int start, int end, int head_tone, int nucleus_tone)
{
tone_pitch_env = tn->pitch_env0;
drop = tn->tonic_max0 - tn->tonic_min0;
- set_pitch(&syllable_tab[ix++],tn->tonic_min0 << 8,drop << 8);
+ set_pitch(&syllable_tab[ix++],tn->tonic_min0, drop);
}
else
{
tone_pitch_env = tn->pitch_env1;
drop = tn->tonic_max1 - tn->tonic_min1;
- set_pitch(&syllable_tab[ix++],tn->tonic_min1 << 8,drop << 8);
+ set_pitch(&syllable_tab[ix++],tn->tonic_min1, drop);
}
syllable_tab[tone_posn].env = tone_pitch_env;
@@ -695,8 +912,8 @@ static int calc_pitches(int start, int end, int head_tone, int nucleus_tone)
/* tail, after the tonic syllable */
/**********************************/
-
- calc_pitch_segment2(ix, end, tn->tail_start, tn->tail_end, 0);
+
+ SetPitchGradient(ix, end, tn->tail_start, tn->tail_end);
return(tone_pitch_env);
} /* end of calc_pitches */
@@ -720,7 +937,7 @@ static void CalcPitches_Tone(Translator *tr, int clause_tone)
PHONEME_TAB *tph;
PHONEME_TAB *prev_tph; // forget across word boundary
PHONEME_TAB *prevw_tph; // remember across word boundary
- PHONEME_TAB *prev2_tph; // 2 tones previous
+// PHONEME_TAB *prev2_tph; // 2 tones previous
PHONEME_LIST *prev_p;
int pitch_adjust = 0; // pitch gradient through the clause - inital value
@@ -728,8 +945,6 @@ static void CalcPitches_Tone(Translator *tr, int clause_tone)
int pitch_low = 0; // until it drops to this
int pitch_high = 0; // then reset to this
- p = &phoneme_list[0];
-
// count number of stressed syllables
p = &phoneme_list[0];
for(ix=0; ix<n_phoneme_list; ix++, p++)
@@ -822,7 +1037,7 @@ static void CalcPitches_Tone(Translator *tr, int clause_tone)
if(tph->mnemonic == 0x343132) // [214]
prev_p->tone_ph = PhonemeCode2('3','5');
else
- prev_p->tone_ph = PhonemeCode2('2','1');
+ prev_p->tone_ph = PhonemeCode2('2','1');
}
if((prev_tph->mnemonic == 0x3135) && (tph->mnemonic == 0x3135)) // [51] + [51]
{
@@ -840,12 +1055,12 @@ static void CalcPitches_Tone(Translator *tr, int clause_tone)
p->tone_ph = PhonemeCode2('4','4');
// tone 5 is unstressed (shorter)
- p->stresslevel = 1; // diminished stress
+ p->stresslevel = 0; // diminished stress
}
}
prev_p = p;
- prev2_tph = prevw_tph;
+// prev2_tph = prevw_tph;
prevw_tph = prev_tph = tph;
pause = 0;
}
@@ -859,7 +1074,7 @@ static void CalcPitches_Tone(Translator *tr, int clause_tone)
{
tone_ph = p->tone_ph;
- if(p->stresslevel != 1) // TEST, consider all syllables as stressed
+ if(p->stresslevel != 0) // TEST, consider all syllables as stressed
{
if(ix == final_stressed)
{
@@ -900,7 +1115,9 @@ void CalcPitches(Translator *tr, int clause_type)
int n_st;
int option;
int group_tone;
+#ifdef deleted
int group_tone_emph;
+#endif
int group_tone_comma;
int ph_start=0;
int st_start;
@@ -951,11 +1168,24 @@ void CalcPitches(Translator *tr, int clause_type)
option = tr->langopts.intonation_group;
if(option >= INTONATION_TYPES)
- option = 0;
+ option = 1;
- group_tone = tr->punct_to_tone[option][clause_type];
- group_tone_emph = tr->punct_to_tone[option][5]; // emphatic form of statement
- group_tone_comma = tr->punct_to_tone[option][1]; // emphatic form of statement
+ if(option == 0)
+ {
+ group_tone = tr->langopts.tunes[clause_type];
+#ifdef deleted
+ group_tone_emph = tr->langopts.tunes[5];
+#endif
+ group_tone_comma = tr->langopts.tunes[1];
+ }
+ else
+ {
+ group_tone = tr->punct_to_tone[option][clause_type];
+#ifdef deleted
+ group_tone_emph = tr->punct_to_tone[option][5]; // emphatic form of statement
+#endif
+ group_tone_comma = tr->punct_to_tone[option][1]; // emphatic form of statement
+ }
if(clause_type == 4)
no_tonic = 1; /* incomplete clause, used for abbreviations such as Mr. Dr. Mrs. */
@@ -1023,9 +1253,23 @@ void CalcPitches(Translator *tr, int clause_type)
count_pitch_vowels(st_start, ix, n_st);
if((ix < n_st) || (clause_type == 0))
- calc_pitches(st_start, ix, group_tone_emph, group_tone_emph); // split into > 1 tone groups, use emphatic tone
+ {
+ calc_pitches(option, st_start, ix, group_tone); // split into > 1 tone groups
+
+ if((clause_type==1) || (clause_type==2))
+ group_tone = tr->langopts.tunes[1]; // , or ? remainder has comma-tone
+ else
+ group_tone = tr->langopts.tunes[0]; // . or ! remainder has statement tone
+ }
+ else
+ calc_pitches(option, st_start, ix, group_tone);
+
+#ifdef deleted
+ if((ix < n_st) || (clause_type == 0))
+ calc_pitches(option, st_start, ix, group_tone_emph); // split into > 1 tone groups, use emphatic tone
else
- calc_pitches(st_start, ix, group_tone, group_tone);
+ calc_pitches(option, st_start, ix, group_tone);
+#endif
st_start = ix;
}
@@ -1034,7 +1278,7 @@ void CalcPitches(Translator *tr, int clause_type)
// end of clause after this syllable, indicated by a phonPAUSE_CLAUSE phoneme
st_clause_end = st_ix+1;
count_pitch_vowels(st_start, st_clause_end, st_clause_end);
- calc_pitches(st_start, st_clause_end, group_tone_comma, group_tone_comma);
+ calc_pitches(option, st_start, st_clause_end, group_tone_comma);
st_start = st_clause_end;
}
}
@@ -1042,28 +1286,23 @@ void CalcPitches(Translator *tr, int clause_type)
if(st_start < st_ix)
{
count_pitch_vowels(st_start, st_ix, n_st);
- calc_pitches(st_start, st_ix, group_tone, group_tone);
+ calc_pitches(option, st_start, st_ix, group_tone);
}
-
+
// unpack pitch data
st_ix=0;
for(ix=ph_start; ix < ph_end; ix++)
{
p = &phoneme_list[ix];
p->stresslevel = syllable_tab[st_ix].stress;
-
+
if(p->synthflags & SFLAG_SYLLABLE)
{
syl = &syllable_tab[st_ix];
- x = syl->pitch1 - 72;
- if(x < 0) x = 0;
- p->pitch1 = x;
-
- x = syl->pitch2 - 72;
- if(x < 0) x = 0;
- p->pitch2 = x;
+ p->pitch1 = syl->pitch1;
+ p->pitch2 = syl->pitch2;
p->env = PITCHfall;
if(syl->flags & SYL_RISE)
@@ -1094,11 +1333,11 @@ if(p->tone_ph)
{
p->stresslevel |= 8; // emphasized
}
-
+
st_ix++;
}
}
-} // end of Translator::CalcPitches
+} // end of CalcPitches
+
-
diff --git a/navit/support/espeak/klatt.c b/navit/support/espeak/klatt.c
index 8cdbf782b..c191534a5 100644
--- a/navit/support/espeak/klatt.c
+++ b/navit/support/espeak/klatt.c
@@ -22,7 +22,7 @@
* <http://www.gnu.org/licenses/>. *
***************************************************************************/
-// See URL: ftp://svr-ftp.eng.cam.ac.uk/pub/comp.speech/synthesis/klatt.3.04.tar.gz
+// See URL: ftp://svr-ftp.eng.cam.ac.uk/pub/comp.speech/synthesis/klatt.3.04.tar.gz
#include "StdAfx.h"
@@ -58,19 +58,69 @@ static int sample_count;
/* function prototypes for functions private to this file */
static void flutter(klatt_frame_ptr);
-static double sampled_source (void);
+static double sampled_source (int);
static double impulsive_source (void);
static double natural_source (void);
-static void pitch_synch_par_reset (klatt_frame_ptr);
+static void pitch_synch_par_reset (klatt_frame_ptr);
static double gen_noise (double);
static double DBtoLIN (long);
-static void frame_init (klatt_frame_ptr);
+static void frame_init (klatt_frame_ptr);
static void setabc (long,long,resonator_ptr);
static void setzeroabc (long,long,resonator_ptr);
static klatt_frame_t kt_frame;
static klatt_global_t kt_globals;
+#define NUMBER_OF_SAMPLES 100
+
+static int scale_wav_tab[] = {45,38,45,45,55}; // scale output from different voicing sources
+
+// For testing, this can be overwritten in KlattInit()
+ static short natural_samples2[256]= {
+ 2583, 2516, 2450, 2384, 2319, 2254, 2191, 2127,
+ 2067, 2005, 1946, 1890, 1832, 1779, 1726, 1675,
+ 1626, 1579, 1533, 1491, 1449, 1409, 1372, 1336,
+ 1302, 1271, 1239, 1211, 1184, 1158, 1134, 1111,
+ 1089, 1069, 1049, 1031, 1013, 996, 980, 965,
+ 950, 936, 921, 909, 895, 881, 869, 855,
+ 843, 830, 818, 804, 792, 779, 766, 754,
+ 740, 728, 715, 702, 689, 676, 663, 651,
+ 637, 626, 612, 601, 588, 576, 564, 552,
+ 540, 530, 517, 507, 496, 485, 475, 464,
+ 454, 443, 434, 424, 414, 404, 394, 385,
+ 375, 366, 355, 347, 336, 328, 317, 308,
+ 299, 288, 280, 269, 260, 250, 240, 231,
+ 220, 212, 200, 192, 181, 172, 161, 152,
+ 142, 133, 123, 113, 105, 94, 86, 76,
+ 67, 57, 49, 39, 30, 22, 11, 4,
+ -5, -14, -23, -32, -41, -50, -60, -69,
+ -78, -87, -96, -107, -115, -126, -134, -144,
+ -154, -164, -174, -183, -193, -203, -213, -222,
+ -233, -242, -252, -262, -271, -281, -291, -301,
+ -310, -320, -330, -339, -349, -357, -368, -377,
+ -387, -397, -406, -417, -426, -436, -446, -456,
+ -467, -477, -487, -499, -509, -521, -532, -543,
+ -555, -567, -579, -591, -603, -616, -628, -641,
+ -653, -666, -679, -692, -705, -717, -732, -743,
+ -758, -769, -783, -795, -808, -820, -834, -845,
+ -860, -872, -885, -898, -911, -926, -939, -955,
+ -968, -986, -999, -1018, -1034, -1054, -1072, -1094,
+ -1115, -1138, -1162, -1188, -1215, -1244, -1274, -1307,
+ -1340, -1377, -1415, -1453, -1496, -1538, -1584, -1631,
+ -1680, -1732, -1783, -1839, -1894, -1952, -2010, -2072,
+ -2133, -2196, -2260, -2325, -2390, -2456, -2522, -2589,
+};
+ static short natural_samples[100]=
+ {
+ -310,-400,530,356,224,89,23,-10,-58,-16,461,599,536,701,770,
+ 605,497,461,560,404,110,224,131,104,-97,155,278,-154,-1165,
+ -598,737,125,-592,41,11,-247,-10,65,92,80,-304,71,167,-1,122,
+ 233,161,-43,278,479,485,407,266,650,134,80,236,68,260,269,179,
+ 53,140,275,293,296,104,257,152,311,182,263,245,125,314,140,44,
+ 203,230,-235,-286,23,107,92,-91,38,464,443,176,98,-784,-2449,
+ -1891,-1045,-1600,-1462,-1384,-1261,-949,-730
+ };
+
/*
function RESONATOR
@@ -81,7 +131,7 @@ is stored in the globals structure.
static double resonator(resonator_ptr r, double input)
{
double x;
-
+
x = (double) ((double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2);
r->p2 = (double)r->p1;
r->p1 = (double)x;
@@ -92,11 +142,11 @@ static double resonator(resonator_ptr r, double input)
static double resonator2(resonator_ptr r, double input)
{
double x;
-
+
x = (double) ((double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2);
r->p2 = (double)r->p1;
r->p1 = (double)x;
-
+
r->a += r->a_inc;
r->b += r->b_inc;
r->c += r->c_inc;
@@ -105,13 +155,13 @@ static double resonator2(resonator_ptr r, double input)
-/*
+/*
function ANTIRESONATOR
-This is a generic anti-resonator function. The code is the same as resonator
-except that a,b,c need to be set with setzeroabc() and we save inputs in
+This is a generic anti-resonator function. The code is the same as resonator
+except that a,b,c need to be set with setzeroabc() and we save inputs in
p1/p2 rather than outputs. There is currently only one of these - "rnz"
-Output = (rnz.a * input) + (rnz.b * oldin1) + (rnz.c * oldin2)
+Output = (rnz.a * input) + (rnz.b * oldin1) + (rnz.c * oldin2)
*/
#ifdef deleted
@@ -129,7 +179,7 @@ static double antiresonator2(resonator_ptr r, double input)
register double x = (double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2;
r->p2 = (double)r->p1;
r->p1 = (double)input;
-
+
r->a += r->a_inc;
r->b += r->b_inc;
r->c += r->c_inc;
@@ -143,7 +193,7 @@ function FLUTTER
This function adds F0 flutter, as specified in:
-"Analysis, synthesis and perception of voice quality variations among
+"Analysis, synthesis and perception of voice quality variations among
female and male talkers" D.H. Klatt and L.C. Klatt JASA 87(2) February 1990.
Flutter is added by applying a quasi-random element constructed from three
@@ -155,7 +205,7 @@ static void flutter(klatt_frame_ptr frame)
static int time_count;
double delta_f0;
double fla,flb,flc,fld,fle;
-
+
fla = (double) kt_globals.f0_flutter / 50;
flb = (double) kt_globals.original_f0 / 100;
// flc = sin(2*PI*12.7*time_count);
@@ -178,7 +228,7 @@ Allows the use of a glottal excitation waveform sampled from a real
voice.
*/
-static double sampled_source()
+static double sampled_source(int source_num)
{
int itemp;
double ftemp;
@@ -187,23 +237,35 @@ static double sampled_source()
int current_value;
int next_value;
double temp_diff;
-
+ short *samples;
+
+ if(source_num == 0)
+ {
+ samples = natural_samples;
+ kt_globals.num_samples = 100;
+ }
+ else
+ {
+ samples = natural_samples2;
+ kt_globals.num_samples = 256;
+ }
+
if(kt_globals.T0!=0)
{
ftemp = (double) kt_globals.nper;
ftemp = ftemp / kt_globals.T0;
ftemp = ftemp * kt_globals.num_samples;
itemp = (int) ftemp;
-
+
temp_diff = ftemp - (double) itemp;
-
- current_value = kt_globals.natural_samples[itemp];
- next_value = kt_globals.natural_samples[itemp+1];
-
+
+ current_value = samples[itemp];
+ next_value = samples[itemp+1];
+
diff_value = (double) next_value - (double) current_value;
diff_value = diff_value * temp_diff;
-
- result = kt_globals.natural_samples[itemp] + diff_value;
+
+ result = samples[itemp] + diff_value;
result = result * kt_globals.sample_factor;
}
else
@@ -216,16 +278,17 @@ static double sampled_source()
-/*
+/*
function PARWAVE
Converts synthesis parameters to a waveform.
*/
-static int parwave(klatt_frame_ptr frame)
+static int parwave(klatt_frame_ptr frame)
{
double temp;
+ int value;
double outbypas;
double out;
long n4;
@@ -241,23 +304,22 @@ static int parwave(klatt_frame_ptr frame)
static double sourc;
int ix;
- frame_init(frame); /* get parameters for next frame of speech */
-
flutter(frame); /* add f0 flutter */
-#ifdef deleted
+#ifdef LOG_FRAMES
+if(option_log_frames)
{
FILE *f;
- f=fopen("klatt_log","a");
- fprintf(f,"%4dhz %2dAV %4d %3d, %4d %3d, %4d %3d, %4d %3d, %4d, %3d, %4d %3d TLT=%2d\n",frame->F0hz10,frame->AVdb,
- frame->F1hz,frame->B1hz,frame->F2hz,frame->B2hz,frame->F3hz,frame->B3hz,frame->F4hz,frame->B4hz,frame->F5hz,frame->B5hz,frame->F6hz,frame->B6hz,frame->TLTdb);
+ f=fopen("log-klatt","a");
+ fprintf(f,"%4dhz %2dAV %4d %3d, %4d %3d, %4d %3d, %4d %3d, %4d, %3d, FNZ=%3d TLT=%2d\n",frame->F0hz10,frame->AVdb,
+ frame->Fhz[1],frame->Bhz[1],frame->Fhz[2],frame->Bhz[2],frame->Fhz[3],frame->Bhz[3],frame->Fhz[4],frame->Bhz[4],frame->Fhz[5],frame->Bhz[5],frame->Fhz[0],frame->TLTdb);
fclose(f);
}
#endif
/* MAIN LOOP, for each output sample of current frame: */
- for (kt_globals.ns=0; kt_globals.ns<kt_globals.nspfr; kt_globals.ns++)
+ for (kt_globals.ns=0; kt_globals.ns<kt_globals.nspfr; kt_globals.ns++)
{
/* Get low-passed random number for aspiration and frication noise */
noise = gen_noise(noise);
@@ -266,22 +328,22 @@ static int parwave(klatt_frame_ptr frame)
Amplitude modulate noise (reduce noise amplitude during
second half of glottal period) if voicing simultaneously present.
*/
-
- if (kt_globals.nper > kt_globals.nmod)
+
+ if (kt_globals.nper > kt_globals.nmod)
{
noise *= (double) 0.5;
}
-
+
/* Compute frication noise */
frics = kt_globals.amp_frica * noise;
-
+
/*
- Compute voicing waveform. Run glottal source simulation at 4
- times normal sample rate to minimize quantization noise in
+ Compute voicing waveform. Run glottal source simulation at 4
+ times normal sample rate to minimize quantization noise in
period of female voice.
*/
-
- for (n4=0; n4<4; n4++)
+
+ for (n4=0; n4<4; n4++)
{
switch(kt_globals.glsource)
{
@@ -289,64 +351,67 @@ static int parwave(klatt_frame_ptr frame)
voice = impulsive_source();
break;
case NATURAL:
- voice = natural_source();
+ voice = natural_source();
break;
case SAMPLED:
- voice = sampled_source();
+ voice = sampled_source(0);
+ break;
+ case SAMPLED2:
+ voice = sampled_source(1);
break;
}
-
+
/* Reset period when counter 'nper' reaches T0 */
- if (kt_globals.nper >= kt_globals.T0)
+ if (kt_globals.nper >= kt_globals.T0)
{
kt_globals.nper = 0;
pitch_synch_par_reset(frame);
}
-
+
/*
Low-pass filter voicing waveform before downsampling from 4*samrate
- to samrate samples/sec. Resonator f=.09*samrate, bw=.06*samrate
+ to samrate samples/sec. Resonator f=.09*samrate, bw=.06*samrate
*/
-
+
voice = resonator(&(kt_globals.rsn[RLP]),voice);
-
+
/* Increment counter that keeps track of 4*samrate samples per sec */
kt_globals.nper++;
}
-
+
/*
Tilt spectrum of voicing source down by soft low-pass filtering, amount
of tilt determined by TLTdb
*/
-
+
voice = (voice * kt_globals.onemd) + (vlast * kt_globals.decay);
vlast = voice;
-
- /*
- Add breathiness during glottal open phase. Amount of breathiness
- determined by parameter Aturb Use nrand rather than noise because
- noise is low-passed.
+
+ /*
+ Add breathiness during glottal open phase. Amount of breathiness
+ determined by parameter Aturb Use nrand rather than noise because
+ noise is low-passed.
*/
-
-
- if (kt_globals.nper < kt_globals.nopen)
+
+
+ if (kt_globals.nper < kt_globals.nopen)
{
voice += kt_globals.amp_breth * kt_globals.nrand;
}
-
+
/* Set amplitude of voicing */
glotout = kt_globals.amp_voice * voice;
par_glotout = kt_globals.par_amp_voice * voice;
-
+
/* Compute aspiration amplitude and add to voicing source */
aspiration = kt_globals.amp_aspir * noise;
glotout += aspiration;
-
+
par_glotout += aspiration;
-
- /*
+
+ /*
Cascade vocal tract, excited by laryngeal sources.
- Nasal antiresonator, then formants FNP, F5, F4, F3, F2, F1
+ Nasal antiresonator, then formants FNP, F5, F4, F3, F2, F1
*/
out=0;
@@ -363,20 +428,20 @@ static int parwave(klatt_frame_ptr frame)
casc_next_in = resonator2(&(kt_globals.rsn[R2c]),casc_next_in);
out = resonator2(&(kt_globals.rsn[R1c]),casc_next_in);
}
-
+
/* Excite parallel F1 and FNP by voicing waveform */
sourc = par_glotout; /* Source is voicing plus aspiration */
/*
- Standard parallel vocal tract Formants F6,F5,F4,F3,F2,
- outputs added with alternating sign. Sound source for other
- parallel resonators is frication plus first difference of
- voicing waveform.
+ Standard parallel vocal tract Formants F6,F5,F4,F3,F2,
+ outputs added with alternating sign. Sound source for other
+ parallel resonators is frication plus first difference of
+ voicing waveform.
*/
-
+
out += resonator(&(kt_globals.rsn[R1p]),sourc);
out += resonator(&(kt_globals.rsn[Rnpp]),sourc);
-
+
sourc = frics + par_glotout - glotlast;
glotlast = par_glotout;
@@ -384,14 +449,14 @@ static int parwave(klatt_frame_ptr frame)
{
out = resonator(&(kt_globals.rsn[ix]),sourc) - out;
}
-
+
outbypas = kt_globals.amp_bypas * sourc;
-
+
out = outbypas - out;
#ifdef deleted
// for testing
- if (kt_globals.outsl != 0)
+ if (kt_globals.outsl != 0)
{
switch(kt_globals.outsl)
{
@@ -401,7 +466,7 @@ static int parwave(klatt_frame_ptr frame)
case 2:
out = aspiration;
break;
- case 3:
+ case 3:
out = frics;
break;
case 4:
@@ -421,7 +486,7 @@ static int parwave(klatt_frame_ptr frame)
#endif
out = resonator(&(kt_globals.rsn[Rout]),out);
- temp = (out * wdata.amplitude * kt_globals.amp_gain0) ; /* Convert back to integer */
+ temp = (int)(out * wdata.amplitude * kt_globals.amp_gain0) ; /* Convert back to integer */
// mix with a recorded WAV if required for this phoneme
@@ -429,7 +494,7 @@ static int parwave(klatt_frame_ptr frame)
int z2;
signed char c;
int sample;
-
+
z2 = 0;
if(wdata.mix_wavefile_ix < wdata.n_mix_wavefile)
{
@@ -458,18 +523,27 @@ static int parwave(klatt_frame_ptr frame)
temp = (temp * kt_globals.fadeout) / 64;
}
- if (temp < -32768.0)
+ value = (int)temp + ((echo_buf[echo_tail++]*echo_amp) >> 8);
+ if(echo_tail >= N_ECHO_BUF)
+ echo_tail=0;
+
+ if (value < -32768)
{
- temp = -32768.0;
+ value = -32768;
}
-
- if (temp > 32767.0)
+
+ if (value > 32767)
{
- temp = 32767.0;
+ value = 32767;
}
-
- *out_ptr++ = (int)(temp); // **JSD
- *out_ptr++ = (int)(temp) >> 8;
+
+ *out_ptr++ = value;
+ *out_ptr++ = value >> 8;
+
+ echo_buf[echo_head++] = value;
+ if(echo_head >= N_ECHO_BUF)
+ echo_head = 0;
+
sample_count++;
if(out_ptr >= out_end)
{
@@ -482,47 +556,52 @@ static int parwave(klatt_frame_ptr frame)
-/*
-function PARWAVE_INIT
-
-Initialises all parameters used in parwave, sets resonator internal memory
-to zero.
-*/
-static void reset_resonators()
+void KlattReset(int control)
{
int r_ix;
- for(r_ix=0; r_ix < N_RSN; r_ix++)
+ if(control == 2)
+ {
+ //Full reset
+ kt_globals.FLPhz = (950 * kt_globals.samrate) / 10000;
+ kt_globals.BLPhz = (630 * kt_globals.samrate) / 10000;
+ kt_globals.minus_pi_t = -PI / kt_globals.samrate;
+ kt_globals.two_pi_t = -2.0 * kt_globals.minus_pi_t;
+ setabc(kt_globals.FLPhz,kt_globals.BLPhz,&(kt_globals.rsn[RLP]));
+
+ }
+
+ if(control > 0)
+ {
+ kt_globals.nper = 0;
+ kt_globals.T0 = 0;
+ kt_globals.nopen = 0;
+ kt_globals.nmod = 0;
+
+ for(r_ix=RGL; r_ix < N_RSN; r_ix++)
+ {
+ kt_globals.rsn[r_ix].p1 = 0;
+ kt_globals.rsn[r_ix].p2 = 0;
+ }
+
+ }
+
+ for(r_ix=0; r_ix <= R6p; r_ix++)
{
kt_globals.rsn[r_ix].p1 = 0;
kt_globals.rsn[r_ix].p2 = 0;
}
}
-static void parwave_init()
-{
- kt_globals.FLPhz = (950 * kt_globals.samrate) / 10000;
- kt_globals.BLPhz = (630 * kt_globals.samrate) / 10000;
- kt_globals.minus_pi_t = -PI / kt_globals.samrate;
- kt_globals.two_pi_t = -2.0 * kt_globals.minus_pi_t;
- setabc(kt_globals.FLPhz,kt_globals.BLPhz,&(kt_globals.rsn[RLP]));
- kt_globals.nper = 0;
- kt_globals.T0 = 0;
- kt_globals.nopen = 0;
- kt_globals.nmod = 0;
-
- reset_resonators();
-}
-
-/*
+/*
function FRAME_INIT
Use parameters from the input frame to set up resonator coefficients.
*/
-static void frame_init(klatt_frame_ptr frame)
+static void frame_init(klatt_frame_ptr frame)
{
double amp_par[7];
static double amp_par_factor[7] = {0.6, 0.4, 0.15, 0.06, 0.04, 0.022, 0.03};
@@ -530,13 +609,13 @@ static void frame_init(klatt_frame_ptr frame)
int ix;
kt_globals.original_f0 = frame->F0hz10 / 10;
-
+
frame->AVdb_tmp = frame->AVdb - 7;
if (frame->AVdb_tmp < 0)
{
frame->AVdb_tmp = 0;
}
-
+
kt_globals.amp_aspir = DBtoLIN(frame->ASP) * 0.05;
kt_globals.amp_frica = DBtoLIN(frame->AF) * 0.25;
kt_globals.par_amp_voice = DBtoLIN(frame->AVpdb);
@@ -549,14 +628,14 @@ static void frame_init(klatt_frame_ptr frame)
}
Gain0_tmp = frame->Gain0 - 3;
- if (Gain0_tmp <= 0)
+ if (Gain0_tmp <= 0)
{
Gain0_tmp = 57;
}
kt_globals.amp_gain0 = DBtoLIN(Gain0_tmp) / kt_globals.scale_wav;
-
+
/* Set coefficients of variable cascade resonators */
- for(ix=0; ix<=8; ix++)
+ for(ix=1; ix<=9; ix++)
{
// formants 1 to 8, plus nasal pole
setabc(frame->Fhz[ix],frame->Bhz[ix],&(kt_globals.rsn[ix]));
@@ -564,7 +643,7 @@ static void frame_init(klatt_frame_ptr frame)
if(ix <= 5)
{
setabc(frame->Fhz_next[ix],frame->Bhz_next[ix],&(kt_globals.rsn_next[ix]));
-
+
kt_globals.rsn[ix].a_inc = (kt_globals.rsn_next[ix].a - kt_globals.rsn[ix].a) / 64.0;
kt_globals.rsn[ix].b_inc = (kt_globals.rsn_next[ix].b - kt_globals.rsn[ix].b) / 64.0;
kt_globals.rsn[ix].c_inc = (kt_globals.rsn_next[ix].c - kt_globals.rsn[ix].c) / 64.0;
@@ -578,19 +657,19 @@ static void frame_init(klatt_frame_ptr frame)
kt_globals.rsn[F_NZ].b_inc = (kt_globals.rsn_next[F_NZ].b - kt_globals.rsn[F_NZ].b) / 64.0;
kt_globals.rsn[F_NZ].c_inc = (kt_globals.rsn_next[F_NZ].c - kt_globals.rsn[F_NZ].c) / 64.0;
-
+
/* Set coefficients of parallel resonators, and amplitude of outputs */
-
+
for(ix=0; ix<=6; ix++)
{
setabc(frame->Fhz[ix],frame->Bphz[ix],&(kt_globals.rsn[Rparallel+ix]));
kt_globals.rsn[Rparallel+ix].a *= amp_par[ix];
}
-
+
/* output low-pass filter */
-
+
setabc((long)0.0,(long)(kt_globals.samrate/2),&(kt_globals.rsn[Rout]));
-
+
}
@@ -598,27 +677,27 @@ static void frame_init(klatt_frame_ptr frame)
/*
function IMPULSIVE_SOURCE
-Generate a low pass filtered train of impulses as an approximation of
-a natural excitation waveform. Low-pass filter the differentiated impulse
-with a critically-damped second-order filter, time constant proportional
+Generate a low pass filtered train of impulses as an approximation of
+a natural excitation waveform. Low-pass filter the differentiated impulse
+with a critically-damped second-order filter, time constant proportional
to Kopen.
*/
-static double impulsive_source()
+static double impulsive_source()
{
static double doublet[] = {0.0,13000000.0,-13000000.0};
static double vwave;
-
- if (kt_globals.nper < 3)
+
+ if (kt_globals.nper < 3)
{
vwave = doublet[kt_globals.nper];
}
- else
+ else
{
vwave = 0.0;
}
-
+
return(resonator(&(kt_globals.rsn[RGL]),vwave));
}
@@ -631,20 +710,20 @@ Vwave is the differentiated glottal flow waveform, there is a weak
spectral zero around 800 Hz, magic constants a,b reset pitch synchronously.
*/
-static double natural_source()
+static double natural_source()
{
double lgtemp;
static double vwave;
-
- if (kt_globals.nper < kt_globals.nopen)
+
+ if (kt_globals.nper < kt_globals.nopen)
{
kt_globals.pulse_shape_a -= kt_globals.pulse_shape_b;
vwave += kt_globals.pulse_shape_a;
lgtemp=vwave * 0.028;
-
+
return(lgtemp);
}
- else
+ else
{
vwave = 0.0;
return(0.0);
@@ -678,125 +757,125 @@ Assume voicing waveform V(t) has form: k1 t**2 - k2 t**3
potion of the voicing cycle "nopen".
Let integral of dV/dt have no net dc flow --> a = (b * nopen) / 3
-
+
Let maximum of dUg(n)/dn be constant --> b = gain / (nopen * nopen)
meaning as nopen gets bigger, V has bigger peak proportional to n
Thus, to generate the table below for 40 <= nopen <= 263:
-
+
B0[nopen - 40] = 1920000 / (nopen * nopen)
*/
-static void pitch_synch_par_reset(klatt_frame_ptr frame)
+static void pitch_synch_par_reset(klatt_frame_ptr frame)
{
long temp;
double temp1;
static long skew;
- static short B0[224] =
+ static short B0[224] =
{
1200,1142,1088,1038, 991, 948, 907, 869, 833, 799, 768, 738, 710, 683, 658,
634, 612, 590, 570, 551, 533, 515, 499, 483, 468, 454, 440, 427, 415, 403,
391, 380, 370, 360, 350, 341, 332, 323, 315, 307, 300, 292, 285, 278, 272,
265, 259, 253, 247, 242, 237, 231, 226, 221, 217, 212, 208, 204, 199, 195,
192, 188, 184, 180, 177, 174, 170, 167, 164, 161, 158, 155, 153, 150, 147,
- 145, 142, 140, 137, 135, 133, 131, 128, 126, 124, 122, 120, 119, 117, 115,
+ 145, 142, 140, 137, 135, 133, 131, 128, 126, 124, 122, 120, 119, 117, 115,
113,111, 110, 108, 106, 105, 103, 102, 100, 99, 97, 96, 95, 93, 92, 91, 90,
88, 87, 86, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 75, 74, 73, 72, 71,
- 70, 69, 68, 68, 67, 66, 65, 64, 64, 63, 62, 61, 61, 60, 59, 59, 58, 57,
+ 70, 69, 68, 68, 67, 66, 65, 64, 64, 63, 62, 61, 61, 60, 59, 59, 58, 57,
57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48,
47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 41, 41, 40, 40,
39, 39, 38, 38, 38, 38, 37, 37, 36, 36, 36, 36, 35, 35, 35, 35, 34, 34,33,
33, 33, 33, 32, 32, 32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29,
28, 28, 28, 28, 27, 27
};
-
- if (frame->F0hz10 > 0)
+
+ if (frame->F0hz10 > 0)
{
/* T0 is 4* the number of samples in one pitch period */
-
+
kt_globals.T0 = (40 * kt_globals.samrate) / frame->F0hz10;
-
-
+
+
kt_globals.amp_voice = DBtoLIN(frame->AVdb_tmp);
-
+
/* Duration of period before amplitude modulation */
-
+
kt_globals.nmod = kt_globals.T0;
- if (frame->AVdb_tmp > 0)
+ if (frame->AVdb_tmp > 0)
{
kt_globals.nmod >>= 1;
}
-
+
/* Breathiness of voicing waveform */
-
+
kt_globals.amp_breth = DBtoLIN(frame->Aturb) * 0.1;
-
+
/* Set open phase of glottal period where 40 <= open phase <= 263 */
-
+
kt_globals.nopen = 4 * frame->Kopen;
-
+
if ((kt_globals.glsource == IMPULSIVE) && (kt_globals.nopen > 263))
{
kt_globals.nopen = 263;
}
-
- if (kt_globals.nopen >= (kt_globals.T0-1))
+
+ if (kt_globals.nopen >= (kt_globals.T0-1))
{
// printf("Warning: glottal open period cannot exceed T0, truncated\n");
kt_globals.nopen = kt_globals.T0 - 2;
}
-
- if (kt_globals.nopen < 40)
+
+ if (kt_globals.nopen < 40)
{
/* F0 max = 1000 Hz */
// printf("Warning: minimum glottal open period is 10 samples.\n");
// printf("truncated, nopen = %d\n",kt_globals.nopen);
kt_globals.nopen = 40;
}
-
-
+
+
/* Reset a & b, which determine shape of "natural" glottal waveform */
-
+
kt_globals.pulse_shape_b = B0[kt_globals.nopen-40];
kt_globals.pulse_shape_a = (kt_globals.pulse_shape_b * kt_globals.nopen) * 0.333;
-
+
/* Reset width of "impulsive" glottal pulse */
-
+
temp = kt_globals.samrate / kt_globals.nopen;
-
+
setabc((long)0,temp,&(kt_globals.rsn[RGL]));
-
+
/* Make gain at F1 about constant */
-
+
temp1 = kt_globals.nopen *.00833;
kt_globals.rsn[RGL].a *= temp1 * temp1;
-
+
/*
Truncate skewness so as not to exceed duration of closed phase
of glottal period.
*/
-
-
+
+
temp = kt_globals.T0 - kt_globals.nopen;
- if (frame->Kskew > temp)
+ if (frame->Kskew > temp)
{
// printf("Kskew duration=%d > glottal closed period=%d, truncate\n", frame->Kskew, kt_globals.T0 - kt_globals.nopen);
frame->Kskew = temp;
}
- if (skew >= 0)
+ if (skew >= 0)
{
skew = frame->Kskew;
}
- else
+ else
{
skew = - frame->Kskew;
}
-
+
/* Add skewness to closed portion of voicing period */
kt_globals.T0 = kt_globals.T0 + skew;
skew = - skew;
}
- else
+ else
{
kt_globals.T0 = 4; /* Default for f0 undefined */
kt_globals.amp_voice = 0.0;
@@ -805,20 +884,20 @@ static void pitch_synch_par_reset(klatt_frame_ptr frame)
kt_globals.pulse_shape_a = 0.0;
kt_globals.pulse_shape_b = 0.0;
}
-
+
/* Reset these pars pitch synchronously or at update rate if f0=0 */
-
- if ((kt_globals.T0 != 4) || (kt_globals.ns == 0))
+
+ if ((kt_globals.T0 != 4) || (kt_globals.ns == 0))
{
/* Set one-pole low-pass filter that tilts glottal source */
-
+
kt_globals.decay = (0.033 * frame->TLTdb);
-
- if (kt_globals.decay > 0.0)
+
+ if (kt_globals.decay > 0.0)
{
kt_globals.onemd = 1.0 - kt_globals.decay;
}
- else
+ else
{
kt_globals.onemd = 1.0;
}
@@ -830,7 +909,7 @@ static void pitch_synch_par_reset(klatt_frame_ptr frame)
/*
function SETABC
-Convert formant freqencies and bandwidth into resonator difference
+Convert formant freqencies and bandwidth into resonator difference
equation constants.
*/
@@ -839,18 +918,18 @@ static void setabc(long int f, long int bw, resonator_ptr rp)
{
double r;
double arg;
-
+
/* Let r = exp(-pi bw t) */
arg = kt_globals.minus_pi_t * bw;
r = exp(arg);
-
+
/* Let c = -r**2 */
rp->c = -(r * r);
-
+
/* Let b = r * 2*cos(2 pi f t) */
arg = kt_globals.two_pi_t * f;
rp->b = r * cos(arg) * 2.0;
-
+
/* Let a = 1.0 - b - c */
rp->a = 1.0 - rp->b - rp->c;
}
@@ -859,7 +938,7 @@ static void setabc(long int f, long int bw, resonator_ptr rp)
/*
function SETZEROABC
-Convert formant freqencies and bandwidth into anti-resonator difference
+Convert formant freqencies and bandwidth into anti-resonator difference
equation constants.
*/
@@ -867,56 +946,62 @@ static void setzeroabc(long int f, long int bw, resonator_ptr rp)
{
double r;
double arg;
-
+
f = -f;
-
- if(f>=0)
- {
- f = -1;
- }
-
+
+//NOTE, changes made 30.09.2011 for Reece Dunn <msclrhd@googlemail.com>
+// fix a sound spike when f=0
+
/* First compute ordinary resonator coefficients */
/* Let r = exp(-pi bw t) */
arg = kt_globals.minus_pi_t * bw;
r = exp(arg);
-
+
/* Let c = -r**2 */
rp->c = -(r * r);
-
+
/* Let b = r * 2*cos(2 pi f t) */
arg = kt_globals.two_pi_t * f;
rp->b = r * cos(arg) * 2.;
-
+
/* Let a = 1.0 - b - c */
rp->a = 1.0 - rp->b - rp->c;
-
+
/* Now convert to antiresonator coefficients (a'=1/a, b'=b/a, c'=c/a) */
- rp->a = 1.0 / rp->a;
- rp->c *= -rp->a;
- rp->b *= -rp->a;
+ /* If f == 0 then rp->a gets set to 0 which makes a'=1/a set a', b' and c' to
+ * INF, causing an audible sound spike when triggered (e.g. apiration with the
+ * nasal register set to f=0, bw=0).
+ */
+ if (rp->a != 0)
+ {
+ /* Now convert to antiresonator coefficients (a'=1/a, b'=b/a, c'=c/a) */
+ rp->a = 1.0 / rp->a;
+ rp->c *= -rp->a;
+ rp->b *= -rp->a;
+ }
}
-/*
+/*
function GEN_NOISE
-Random number generator (return a number between -8191 and +8191)
-Noise spectrum is tilted down by soft low-pass filter having a pole near
-the origin in the z-plane, i.e. output = input + (0.75 * lastoutput)
+Random number generator (return a number between -8191 and +8191)
+Noise spectrum is tilted down by soft low-pass filter having a pole near
+the origin in the z-plane, i.e. output = input + (0.75 * lastoutput)
*/
-static double gen_noise(double noise)
+static double gen_noise(double noise)
{
long temp;
static double nlast;
-
+
temp = (long) getrandom(-8191,8191);
kt_globals.nrand = (long) temp;
-
+
noise = kt_globals.nrand + (0.75 * nlast);
nlast = noise;
-
+
return(noise);
}
@@ -933,16 +1018,16 @@ Conversion table, db to linear, 87 dB --> 32767
81 dB --> 16384 (6 dB down = 0.5)
...
0 dB --> 0
-
+
The just noticeable difference for a change in intensity of a vowel
is approximately 1 dB. Thus all amplitudes are quantized to 1 dB
steps.
*/
-static double DBtoLIN(long dB)
+static double DBtoLIN(long dB)
{
- static short amptable[88] =
+ static short amptable[88] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7,
8, 9, 10, 11, 13, 14, 16, 18, 20, 22, 25, 28, 32,
@@ -953,12 +1038,12 @@ static double DBtoLIN(long dB)
4096, 4547, 5104, 5751, 6488, 7291, 8192, 9093, 10207,
11502, 12976, 14582, 16384, 18350, 20644, 23429,
26214, 29491, 32767 };
-
+
if ((dB < 0) || (dB > 87))
{
return(0);
}
-
+
return((double)(amptable[dB]) * 0.001);
}
@@ -973,15 +1058,15 @@ static int klattp[N_KLATTP];
static double klattp1[N_KLATTP];
static double klattp_inc[N_KLATTP];
-static int scale_wav_tab[] = {45,38,45,45}; // scale output from different voicing sources
-int Wavegen_Klatt(int resume)
+static int Wavegen_Klatt(int resume)
{//==========================
int pk;
int x;
int ix;
+ int fade;
if(resume==0)
{
@@ -1005,7 +1090,7 @@ int Wavegen_Klatt(int resume)
}
for(ix=1; ix < 7; ix++)
{
- kt_frame.Ap[ix] = 0;
+ kt_frame.Ap[ix] = peaks[ix].ap;
}
kt_frame.AVdb = klattp[KLATT_AV];
@@ -1035,7 +1120,7 @@ int Wavegen_Klatt(int resume)
for(ix=0; ix < N_KLATTP; ix++)
{
klattp1[ix] += klattp_inc[ix];
- klattp[ix] = (int)(klattp1[ix]);
+ klattp[ix] = (int)klattp1[ix];
}
for(ix=0; ix<=6; ix++)
@@ -1057,22 +1142,37 @@ int Wavegen_Klatt(int resume)
if(kt_globals.nspfr > STEPSIZE)
kt_globals.nspfr = STEPSIZE;
+ frame_init(&kt_frame); /* get parameters for next frame of speech */
+
if(parwave(&kt_frame) == 1)
{
- return(1);
+ return(1); // output buffer is full
}
}
- if(end_wave == 1)
+ if(end_wave > 0)
{
+#ifdef deleted
+ if(end_wave == 2)
+ {
+ fade = (kt_globals.T0 - kt_globals.nper)/4; // samples until end of current cycle
+ if(fade < 64)
+ fade = 64;
+ }
+ else
+#endif
+ {
+ fade = 64; // not followd by formant synthesis
+ }
+
// fade out to avoid a click
- kt_globals.fadeout = 64;
+ kt_globals.fadeout = fade;
end_wave = 0;
- sample_count -= 64;
- kt_globals.nspfr = 64;
+ sample_count -= fade;
+ kt_globals.nspfr = fade;
if(parwave(&kt_frame) == 1)
{
- return(1);
+ return(1); // output buffer is full
}
}
@@ -1080,17 +1180,18 @@ int Wavegen_Klatt(int resume)
}
-void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v, int control)
+static void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v, int control)
{//===========================================================================================
int ix;
DOUBLEX next;
int qix;
int cmd;
+ frame_t *fr3;
static frame_t prev_fr;
if(wvoice != NULL)
{
- if((wvoice->klattv[0] > 0) && (wvoice->klattv[0] <=3 ))
+ if((wvoice->klattv[0] > 0) && (wvoice->klattv[0] <=4 ))
{
kt_globals.glsource = wvoice->klattv[0];
kt_globals.scale_wav = scale_wav_tab[kt_globals.glsource];
@@ -1110,11 +1211,22 @@ void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v
{
if(qix >= N_WCMDQ) qix = 0;
if(qix == wcmdq_tail) break;
-
+
cmd = wcmdq[qix][0];
if(cmd==WCMD_KLATT)
{
end_wave = 0; // next wave generation is from another spectrum
+
+ fr3 = (frame_t *)wcmdq[qix][2];
+ for(ix=1; ix<6; ix++)
+ {
+ if(fr3->ffreq[ix] != fr2->ffreq[ix])
+ {
+ // there is a discontinuity in formants
+ end_wave = 2;
+ break;
+ }
+ }
break;
}
if((cmd==WCMD_WAVE) || (cmd==WCMD_PAUSE))
@@ -1122,47 +1234,44 @@ void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v
}
}
+#ifdef LOG_FRAMES
+if(option_log_frames)
{
-//FILE *f;
-//f=fopen("klatt_log","a");
-//fprintf(f,"len %4d (%3d %4d %4d) (%3d %4d %4d)\n",length,fr1->ffreq[1],fr1->ffreq[2],fr1->ffreq[3],fr2->ffreq[1],fr2->ffreq[2],fr2->ffreq[3]);
-//fclose(f);
+ FILE *f_log;
+ f_log=fopen("log-espeakedit","a");
+ if(f_log != NULL)
+ {
+ fprintf(f_log,"K %3dmS %3d %3d %4d %4d %4d %4d (%2d) to %3d %3d %4d %4d %4d %4d (%2d)\n",length*1000/samplerate,
+ fr1->klattp[KLATT_FNZ]*2,fr1->ffreq[1],fr1->ffreq[2],fr1->ffreq[3],fr1->ffreq[4],fr1->ffreq[5], fr1->klattp[KLATT_AV],
+ fr2->klattp[KLATT_FNZ]*2,fr2->ffreq[1],fr2->ffreq[2],fr2->ffreq[3],fr1->ffreq[4],fr1->ffreq[5], fr2->klattp[KLATT_AV] );
+ fclose(f_log);
+ }
+ f_log=fopen("log-klatt","a");
+ if(f_log != NULL)
+ {
+ fprintf(f_log,"K %3dmS %3d %3d %4d %4d (%2d) to %3d %3d %4d %4d (%2d)\n",length*1000/samplerate,
+ fr1->klattp[KLATT_FNZ]*2,fr1->ffreq[1],fr1->ffreq[2],fr1->ffreq[3], fr1->klattp[KLATT_AV],
+ fr2->klattp[KLATT_FNZ]*2,fr2->ffreq[1],fr2->ffreq[2],fr2->ffreq[3], fr2->klattp[KLATT_AV] );
+
+ fclose(f_log);
+ }
}
+#endif
if(control & 1)
{
- if(wdata.prev_was_synth == 0)
- {
- // A break, not following on from another synthesized sound.
- // Reset the synthesizer
- //reset_resonators(&kt_globals);
- parwave_init();
- }
- else
+ for(ix=1; ix<6; ix++)
{
- if((prev_fr.ffreq[1] != fr1->ffreq[1]) || (prev_fr.ffreq[2] != fr1->ffreq[2]))
+ if(prev_fr.ffreq[ix] != fr1->ffreq[ix])
{
-
- // fade out to avoid a click, but only up to the end of output buffer
- ix = (out_end - out_ptr)/2;
- if(ix > 64)
- ix = 64;
- kt_globals.fadeout = ix;
- kt_globals.nspfr = ix;
- parwave(&kt_frame);
-
- //reset_resonators(&kt_globals);
- parwave_init();
+ // Discontinuity in formants.
+ // end_wave was set in SetSynth_Klatt() to fade out the previous frame
+ KlattReset(0);
+ break;
}
}
- wdata.prev_was_synth = 1;
memcpy(&prev_fr,fr2,sizeof(prev_fr));
}
- if(fr2->frflags & FRFLAG_BREAK)
- {
-// fr2 = fr1;
-// reset_resonators(&kt_globals);
- }
for(ix=0; ix<N_KLATTP; ix++)
{
@@ -1187,7 +1296,7 @@ void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v
for(ix=1; ix < 6; ix++)
{
peaks[ix].freq1 = (fr1->ffreq[ix] * v->freq[ix] / 256.0) + v->freqadd[ix];
- peaks[ix].freq = (int)(peaks[ix].freq1);
+ peaks[ix].freq = (int)peaks[ix].freq1;
next = (fr2->ffreq[ix] * v->freq[ix] / 256.0) + v->freqadd[ix];
peaks[ix].freq_inc = ((next - peaks[ix].freq1) * STEPSIZE) / length;
@@ -1195,7 +1304,7 @@ void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v
{
// klatt bandwidth for f1, f2, f3 (others are fixed)
peaks[ix].bw1 = fr1->bw[ix] * 2;
- peaks[ix].bw = (int)(peaks[ix].bw1);
+ peaks[ix].bw = (int)peaks[ix].bw1;
next = fr2->bw[ix] * 2;
peaks[ix].bw_inc = ((next - peaks[ix].bw1) * STEPSIZE) / length;
}
@@ -1203,8 +1312,14 @@ void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v
// nasal zero frequency
peaks[0].freq1 = fr1->klattp[KLATT_FNZ] * 2;
- peaks[0].freq = (int)(peaks[0].freq1);
+ if(peaks[0].freq1 == 0)
+ peaks[0].freq1 = kt_frame.Fhz[F_NP]; // if no nasal zero, set it to same freq as nasal pole
+
+ peaks[0].freq = (int)peaks[0].freq1;
next = fr2->klattp[KLATT_FNZ] * 2;
+ if(next == 0)
+ next = kt_frame.Fhz[F_NP];
+
peaks[0].freq_inc = ((next - peaks[0].freq1) * STEPSIZE) / length;
peaks[0].bw1 = 89;
@@ -1217,13 +1332,13 @@ void SetSynth_Klatt(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v
for(ix=1; ix < 7; ix++)
{
peaks[ix].bp1 = fr1->klatt_bp[ix] * 4; // parallel bandwidth
- peaks[ix].bp = (int)(peaks[ix].bp1);
- next = fr2->klatt_bp[ix] * 2;
+ peaks[ix].bp = (int)peaks[ix].bp1;
+ next = fr2->klatt_bp[ix] * 4;
peaks[ix].bp_inc = ((next - peaks[ix].bp1) * STEPSIZE) / length;
peaks[ix].ap1 = fr1->klatt_ap[ix]; // parallal amplitude
- peaks[ix].ap = (int)(peaks[ix].ap1);
- next = fr2->klatt_ap[ix] * 2;
+ peaks[ix].ap = (int)peaks[ix].ap1;
+ next = fr2->klatt_ap[ix];
peaks[ix].ap_inc = ((next - peaks[ix].ap1) * STEPSIZE) / length;
}
}
@@ -1242,18 +1357,7 @@ int Wavegen_Klatt2(int length, int modulation, int resume, frame_t *fr1, frame_t
void KlattInit()
{
-#define NUMBER_OF_SAMPLES 100
- static short natural_samples[NUMBER_OF_SAMPLES]=
- {
- -310,-400,530,356,224,89,23,-10,-58,-16,461,599,536,701,770,
- 605,497,461,560,404,110,224,131,104,-97,155,278,-154,-1165,
- -598,737,125,-592,41,11,-247,-10,65,92,80,-304,71,167,-1,122,
- 233,161,-43,278,479,485,407,266,650,134,80,236,68,260,269,179,
- 53,140,275,293,296,104,257,152,311,182,263,245,125,314,140,44,
- 203,230,-235,-286,23,107,92,-91,38,464,443,176,98,-784,-2449,
- -1891,-1045,-1600,-1462,-1384,-1261,-949,-730
- };
static short formant_hz[10] = {280,688,1064,2806,3260,3700,6500,7000,8000,280};
static short bandwidth[10] = {89,160,70,160,200,200,500,500,500,89};
static short parallel_amp[10] = { 0,59,59,59,59,59,59,0,0,0};
@@ -1261,6 +1365,12 @@ void KlattInit()
int ix;
+for(ix=0; ix<256; ix++)
+{
+ // TEST: Overwrite natural_samples2
+ // sawtooth wave
+// natural_samples2[ix] = (128-ix) * 20;
+}
sample_count=0;
kt_globals.synthesis_model = CASCADE_PARALLEL;
@@ -1275,7 +1385,7 @@ void KlattInit()
kt_globals.outsl = 0;
kt_globals.f0_flutter = 20;
- parwave_init();
+ KlattReset(2);
// set default values for frame parameters
for(ix=0; ix<=9; ix++)
@@ -1297,7 +1407,7 @@ void KlattInit()
kt_frame.Kskew = 0;
kt_frame.AB = 0;
kt_frame.AVpdb = 0;
- kt_frame.Gain0 = 60; // 62
+ kt_frame.Gain0 = 62; // 60
} // end of KlattInit
#endif // INCLUDE_KLATT
diff --git a/navit/support/espeak/klatt.h b/navit/support/espeak/klatt.h
index 5583b178a..e4448e4a1 100644
--- a/navit/support/espeak/klatt.h
+++ b/navit/support/espeak/klatt.h
@@ -1,11 +1,12 @@
#define CASCADE_PARALLEL 1 /* Type of synthesis model */
-#define ALL_PARALLEL 2
+#define ALL_PARALLEL 2
#define IMPULSIVE 1 /* Type of voicing source */
#define NATURAL 2
#define SAMPLED 3
+#define SAMPLED2 4
#define PI 3.1415927
@@ -68,7 +69,7 @@ typedef struct
int scale_wav; // depends on the voicing source
#define N_RSN 20
-#define Rnpc 0
+#define Rnz 0 // nasal zero, anti-resonator
#define R1c 1
#define R2c 2
#define R3c 3
@@ -77,7 +78,7 @@ typedef struct
#define R6c 6
#define R7c 7
#define R8c 8
-#define Rnz 9
+#define Rnpc 9 // nasal pole
#define Rparallel 10
#define Rnpp 10
@@ -96,7 +97,7 @@ typedef struct
resonator_t rsn_next[N_RSN];
} klatt_global_t, *klatt_global_ptr;
-
+
/* Structure for Klatt Parameters */
#define F_NZ 0 // nasal zero formant
@@ -111,25 +112,25 @@ typedef struct
typedef struct
{
- long F0hz10; /* Voicing fund freq in Hz */
- long AVdb; /* Amp of voicing in dB, 0 to 70 */
+ int F0hz10; /* Voicing fund freq in Hz */
+ int AVdb; /* Amp of voicing in dB, 0 to 70 */
int Fhz[10]; // formant Hz, F_NZ to F6 to F_NP
int Bhz[10];
int Ap[10]; /* Amp of parallel formants in dB, 0 to 80 */
int Bphz[10]; /* Parallel formants bw in Hz, 40 to 1000 */
- long ASP; /* Amp of aspiration in dB, 0 to 70 */
- long Kopen; /* # of samples in open period, 10 to 65 */
- long Aturb; /* Breathiness in voicing, 0 to 80 */
- long TLTdb; /* Voicing spectral tilt in dB, 0 to 24 */
- long AF; /* Amp of frication in dB, 0 to 80 */
- long Kskew; /* Skewness of alternate periods, 0 to 40 in sample#/2 */
+ int ASP; /* Amp of aspiration in dB, 0 to 70 */
+ int Kopen; /* # of samples in open period, 10 to 65 */
+ int Aturb; /* Breathiness in voicing, 0 to 80 */
+ int TLTdb; /* Voicing spectral tilt in dB, 0 to 24 */
+ int AF; /* Amp of frication in dB, 0 to 80 */
+ int Kskew; /* Skewness of alternate periods, 0 to 40 in sample#/2 */
- long AB; /* Amp of bypass fric. in dB, 0 to 80 */
- long AVpdb; /* Amp of voicing, par in dB, 0 to 70 */
- long Gain0; /* Overall gain, 60 dB is unity, 0 to 60 */
+ int AB; /* Amp of bypass fric. in dB, 0 to 80 */
+ int AVpdb; /* Amp of voicing, par in dB, 0 to 70 */
+ int Gain0; /* Overall gain, 60 dB is unity, 0 to 60 */
- long AVdb_tmp; //copy of AVdb, which is changed within parwave()
+ int AVdb_tmp; //copy of AVdb, which is changed within parwave()
int Fhz_next[10]; // Fhz for the next chunk, so we can do interpolation of resonator (a,b,c) parameters
int Bhz_next[10];
} klatt_frame_t, *klatt_frame_ptr;
diff --git a/navit/support/espeak/mbrolib.h b/navit/support/espeak/mbrolib.h
deleted file mode 100644
index 0616b464b..000000000
--- a/navit/support/espeak/mbrolib.h
+++ /dev/null
@@ -1,205 +0,0 @@
-#ifndef MBROLIB_H
-#define MBROLIB_H
-
-/*
- * mbrolib: mbrola wrapper.
- *
- * Copyright (C) 2007 Gilles Casse <gcasse@oralux.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
-*/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* < types */
-
-/** Parameters */
-
-typedef struct {
- int ignore_error; /* 1=Ignore any fatal error or unknown diphone */
- char comment_char; /* Comment character */
- float volume_ratio; /* Volume ratio */
- float frequency_ratio; /* Applied to pitch points */
- float time_ratio; /* Applied to phone durations */
-} mbrolib_parameter;
-
-
-/** Returned errors */
-
-typedef enum {
- MBROLIB_OK=0,
- MBROLIB_DATABASE_NOT_INSTALLED,
- MBROLIB_INVAL,
- MBROLIB_OUT_OF_MEMORY,
- MBROLIB_OUT_OF_RANGE,
- MBROLIB_READ_ERROR,
- MBROLIB_WRITE_ERROR
-} MBROLIB_ERROR;
-
-
-
-/** Gender */
-
-typedef enum {
- MBROLIB_FEMALE,
- MBROLIB_MALE
-} MBROLIB_GENDER;
-
-
-
-/** Voice descriptor */
-
-typedef struct {
- char *name; /* name (for example: "en1") */
- char *filename; /* database pathname (for example: "/usr/share/mbrola/voices/en1) */
- int rate; /* database sample rate */
- MBROLIB_GENDER gender;
- const char *language; /* Language and optional dialect qualifier in ascii (e.g. en, fr_ca). */
-} mbrolib_voice;
-
-/* > */
-
-
-/** Initialization, returns a new handle.
- First function.
-
- @param the_sample_rate: output rate in Hz (for example 22050). If 0, keep the original database rate.
-
- @return handle (or NULL if error).
-*/
-void* mbrolib_init( int sample_rate);
-typedef void* (t_mbrolib_init)(int);
-
-
-/** Returns the list of the installed mbrola databases.
- The databases are searched according to the MBROLA_PATH environment variable if set,
- or under a default path otherwise (see MBROLA_PATH in mbrolib.c).
-
- An array of voices is returned. The last item is set to NULL.
- The caller must not free the returned items or the array.
-
- @param the_handle previously given by mbrolib_init.
-
- @return An array of voices.
-*/
-const mbrolib_voice ** mbrolib_list_voices( void* the_handle);
-typedef const mbrolib_voice ** (t_mbrolib_list_voices)(void*);
-
-
-
-/** Set voice
-
- @param the_handle.
-
- @param the_database (for example, "en1").
-
- @return error code (MBROLIB_OK, MBROLIB_DATABASE_NOT_INSTALLED, MBROLIB_INVAL).
-
-*/
-MBROLIB_ERROR mbrolib_set_voice( void* the_handle, const char* the_name);
-typedef MBROLIB_ERROR (t_mbrolib_set_voice)( void*, const char*);
-
-
-
-/** Get the current database parameters.
- The caller supplies a pointer to an already allocated structure.
-
- @param the_handle previously given by mbrolib_init.
-
- @param the_parameters: pointer to the structure.
-
- @return error code (MBROLIB_OK, MBROLIB_INVAL).
-*/
-MBROLIB_ERROR mbrolib_get_parameter(void* the_handle, mbrolib_parameter* the_parameter);
-typedef MBROLIB_ERROR (t_mbrolib_get_parameter)(void*, mbrolib_parameter*);
-
-
-
-/** Set the database parameters using the supplied data.
-
- @param the_handle previously given by mbrolib_init.
-
- @param the_parameters: pointer to the wished parameters.
-
- @return error code (MBROLIB_OK, MBROLIB_INVAL).
-*/
-MBROLIB_ERROR mbrolib_set_parameter(void* the_handle, const mbrolib_parameter* the_parameter);
-typedef MBROLIB_ERROR (t_mbrolib_set_parameter)(void*, const mbrolib_parameter*);
-
-
-
-/** Write the mbrola phonemes in the internal buffer.
-
- @param the_handle.
-
- @param the_mbrola_phonemes.
-
- @param the_size in bytes.
-
- @return error code (MBROLIB_OK, MBROLIB_INVAL, MBROLIB_WRITE_ERROR, MBROLIB_READ_ERROR).
-*/
-MBROLIB_ERROR mbrolib_write(void* the_handle, const char* the_mbrola_phonemes, size_t the_size);
-typedef MBROLIB_ERROR (t_mbrolib_write)(void*, const char*, size_t);
-
-
-
-/** Read n bytes of the output samples.
-
- @param the_handle.
-
- @param the_samples (raw audio data, 16bits, mono).
-
- @param the_size max number of int16 to read.
-
- @param the_size number of int16 read.
-
- @return error code (MBROLIB_OK, MBROLIB_INVAL, MBROLIB_READ_ERROR).
-
-*/
-MBROLIB_ERROR mbrolib_read(void* the_handle, short* the_samples, int the_max_size, int* the_read_size);
-typedef MBROLIB_ERROR (t_mbrolib_read)(void*, short*, int, int*);
-
-
-
-/** Flush
-
- @param the_handle.
-
-*/
-void mbrolib_flush(void* the_handle);
-typedef void (t_mbrolib_flush)(void*);
-
-
-
-/** Release the handle
-
- @param the_handle.
-
- @return error code (MBROLIB_OK, MBROLIB_INVAL).
-
-*/
-MBROLIB_ERROR mbrolib_terminate(void* the_handle);
-typedef MBROLIB_ERROR (t_mbrolib_terminate)(void*);
-
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/navit/support/espeak/mbrowrap.c b/navit/support/espeak/mbrowrap.c
new file mode 100644
index 000000000..9aef6d242
--- /dev/null
+++ b/navit/support/espeak/mbrowrap.c
@@ -0,0 +1,613 @@
+/*
+ * mbrowrap -- A wrapper library around the mbrola binary
+ * providing a subset of the API from the Windows mbrola DLL.
+ *
+ * Copyright (C) 2010 by Nicolas Pitre <nico@fluxnic.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "speech.h"
+#ifdef INCLUDE_MBROLA
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "mbrowrap.h"
+
+
+/*
+ * mbrola instance parameters
+ */
+
+enum mbr_state {
+ MBR_INACTIVE = 0,
+ MBR_IDLE,
+ MBR_NEWDATA,
+ MBR_AUDIO,
+ MBR_WEDGED
+};
+
+static enum mbr_state mbr_state;
+
+static char *mbr_voice_path;
+static int mbr_cmd_fd, mbr_audio_fd, mbr_error_fd, mbr_proc_stat;
+static pid_t mbr_pid;
+static int mbr_samplerate;
+static float mbr_volume = 1.0;
+static char mbr_errorbuf[160];
+
+struct datablock {
+ struct datablock *next;
+ int done;
+ int size;
+ char buffer[1]; /* 1 or more, dynamically allocated */
+};
+
+static struct datablock *mbr_pending_data_head, *mbr_pending_data_tail;
+
+/*
+ * Private support code.
+ */
+
+static void mbro_log(const char *msg, ...)
+{
+ va_list params;
+
+ va_start(params, msg);
+ vfprintf(stderr, msg, params);
+ fputc('\n', stderr);
+ va_end(params);
+}
+
+static void err(const char *errmsg, ...)
+{
+ va_list params;
+
+ va_start(params, errmsg);
+ vsnprintf(mbr_errorbuf, sizeof(mbr_errorbuf), errmsg, params);
+ va_end(params);
+ mbro_log("mbrowrap error: %s", mbr_errorbuf);
+}
+
+static int create_pipes(int p1[2], int p2[2], int p3[2])
+{
+ int error;
+
+ if (pipe(p1) != -1) {
+ if (pipe(p2) != -1) {
+ if (pipe(p3) != -1) {
+ return 0;
+ } else
+ error = errno;
+ close(p2[0]);
+ close(p2[1]);
+ } else
+ error = errno;
+ close(p1[0]);
+ close(p1[1]);
+ } else
+ error = errno;
+
+ err("pipe(): %s", strerror(error));
+ return -1;
+}
+
+static void close_pipes(int p1[2], int p2[2], int p3[2])
+{
+ close(p1[0]);
+ close(p1[1]);
+ close(p2[0]);
+ close(p2[1]);
+ close(p3[0]);
+ close(p3[1]);
+}
+
+static int start_mbrola(const char *voice_path)
+{
+ int error, p_stdin[2], p_stdout[2], p_stderr[2];
+ ssize_t written;
+ char charbuf[20];
+
+ if (mbr_state != MBR_INACTIVE) {
+ err("mbrola init request when already initialized");
+ return -1;
+ }
+
+ error = create_pipes(p_stdin, p_stdout, p_stderr);
+ if (error)
+ return -1;
+
+ mbr_pid = fork();
+
+ if (mbr_pid == -1) {
+ error = errno;
+ close_pipes(p_stdin, p_stdout, p_stderr);
+ err("fork(): %s", strerror(error));
+ return -1;
+ }
+
+ if (mbr_pid == 0) {
+ int i;
+
+ if (dup2(p_stdin[0], 0) == -1 ||
+ dup2(p_stdout[1], 1) == -1 ||
+ dup2(p_stderr[1], 2) == -1) {
+ snprintf(mbr_errorbuf, sizeof(mbr_errorbuf),
+ "dup2(): %s\n", strerror(errno));
+ written = write(p_stderr[1], mbr_errorbuf, strlen(mbr_errorbuf));
+ (void)written; // suppress 'variable not used' warning
+ _exit(1);
+ }
+
+ for (i = p_stderr[1]; i > 2; i--)
+ close(i);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+
+ snprintf(charbuf, sizeof(charbuf), "%g", mbr_volume);
+ execlp("mbrola", "mbrola", "-e", "-v", charbuf,
+ voice_path, "-", "-.wav", (char *)NULL);
+ /* if execution reaches this point then the exec() failed */
+ snprintf(mbr_errorbuf, sizeof(mbr_errorbuf),
+ "mbrola: %s\n", strerror(errno));
+ written = write(2, mbr_errorbuf, strlen(mbr_errorbuf));
+ (void)written; // suppress 'variable not used' warning
+ _exit(1);
+ }
+
+ snprintf(charbuf, sizeof(charbuf), "/proc/%d/stat", mbr_pid);
+ mbr_proc_stat = open(charbuf, O_RDONLY);
+ if (mbr_proc_stat == -1) {
+ error = errno;
+ close_pipes(p_stdin, p_stdout, p_stderr);
+ waitpid(mbr_pid, NULL, 0);
+ mbr_pid = 0;
+ err("/proc is unaccessible: %s", strerror(error));
+ return -1;
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ if (fcntl(p_stdin[1], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl(p_stdout[0], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl(p_stderr[0], F_SETFL, O_NONBLOCK) == -1) {
+ error = errno;
+ close_pipes(p_stdin, p_stdout, p_stderr);
+ waitpid(mbr_pid, NULL, 0);
+ mbr_pid = 0;
+ err("fcntl(): %s", strerror(error));
+ return -1;
+ }
+
+ mbr_cmd_fd = p_stdin[1];
+ mbr_audio_fd = p_stdout[0];
+ mbr_error_fd = p_stderr[0];
+ close(p_stdin[0]);
+ close(p_stdout[1]);
+ close(p_stderr[1]);
+
+ mbr_state = MBR_IDLE;
+ return 0;
+}
+
+static void stop_mbrola(void)
+{
+ if (mbr_state == MBR_INACTIVE)
+ return;
+ close(mbr_proc_stat);
+ close(mbr_cmd_fd);
+ close(mbr_audio_fd);
+ close(mbr_error_fd);
+ if (mbr_pid) {
+ kill(mbr_pid, SIGTERM);
+ waitpid(mbr_pid, NULL, 0);
+ mbr_pid = 0;
+ }
+ mbr_state = MBR_INACTIVE;
+}
+
+static void free_pending_data(void)
+{
+ struct datablock *p, *head = mbr_pending_data_head;
+ while (head) {
+ p = head;
+ head = head->next;
+ free(p);
+ }
+ mbr_pending_data_head = NULL;
+ mbr_pending_data_tail = NULL;
+}
+
+static int mbrola_died(void)
+{
+ pid_t pid;
+ int status, len;
+ const char *msg;
+ char msgbuf[80];
+
+ pid = waitpid(mbr_pid, &status, WNOHANG);
+ if (!pid) {
+ msg = "mbrola closed stderr and did not exit";
+ } else if (pid != mbr_pid) {
+ msg = "waitpid() is confused";
+ } else {
+ mbr_pid = 0;
+ if (WIFSIGNALED(status)) {
+ int sig = WTERMSIG(status);
+ snprintf(msgbuf, sizeof(msgbuf),
+ "mbrola died by signal %d", sig);
+ msg = msgbuf;
+ } else if (WIFEXITED(status)) {
+ int exst = WEXITSTATUS(status);
+ snprintf(msgbuf, sizeof(msgbuf),
+ "mbrola exited with status %d", exst);
+ msg = msgbuf;
+ } else {
+ msg = "mbrola died and wait status is weird";
+ }
+ }
+
+ mbro_log("mbrowrap error: %s", msg);
+
+ len = strlen(mbr_errorbuf);
+ if (!len)
+ snprintf(mbr_errorbuf, sizeof(mbr_errorbuf), "%s", msg);
+ else
+ snprintf(mbr_errorbuf + len, sizeof(mbr_errorbuf) - len,
+ ", (%s)", msg);
+ return -1;
+}
+
+static int mbrola_has_errors(void)
+{
+ int result;
+ char buffer[256];
+ char *buf_ptr, *lf;
+
+ buf_ptr = buffer;
+ for (;;) {
+ result = read(mbr_error_fd, buf_ptr,
+ sizeof(buffer) - (buf_ptr - buffer) - 1);
+ if (result == -1) {
+ if (errno == EAGAIN)
+ return 0;
+ err("read(error): %s", strerror(errno));
+ return -1;
+ }
+
+ if (result == 0) {
+ /* EOF on stderr, assume mbrola died. */
+ return mbrola_died();
+ }
+
+ buf_ptr[result] = 0;
+
+ for (; (lf = strchr(buf_ptr, '\n')); buf_ptr = lf + 1) {
+ /* inhibit the reset signal messages */
+ if (strncmp(buf_ptr, "Got a reset signal", 18) == 0 ||
+ strncmp(buf_ptr, "Input Flush Signal", 18) == 0)
+ continue;
+ *lf = 0;
+ mbro_log("mbrola: %s", buf_ptr);
+ /* is this the last line? */
+ if (lf == &buf_ptr[result - 1]) {
+ snprintf(mbr_errorbuf, sizeof(mbr_errorbuf),
+ "%s", buf_ptr);
+ /* don't consider this fatal at this point */
+ return 0;
+ }
+ }
+
+ memmove(buffer, buf_ptr, result);
+ buf_ptr = buffer + result;
+ }
+}
+
+static int send_to_mbrola(const char *cmd)
+{
+ ssize_t result;
+ int len;
+
+ if (!mbr_pid)
+ return -1;
+
+ len = strlen(cmd);
+ result = write(mbr_cmd_fd, cmd, len);
+
+ if (result == -1) {
+ int error = errno;
+ if (error == EPIPE && mbrola_has_errors()) {
+ return -1;
+ } else if (error == EAGAIN) {
+ result = 0;
+ } else {
+ err("write(): %s", strerror(error));
+ return -1;
+ }
+ }
+
+ if (result != len) {
+ struct datablock *data;
+ data = (struct datablock *)malloc(sizeof(*data) + len - result);
+ if (data) {
+ data->next = NULL;
+ data->done = 0;
+ data->size = len - result;
+ memcpy(data->buffer, cmd + result, len - result);
+ result = len;
+ if (!mbr_pending_data_head)
+ mbr_pending_data_head = data;
+ else
+ mbr_pending_data_tail->next = data;
+ mbr_pending_data_tail = data;
+ }
+ }
+
+ return result;
+}
+
+static int mbrola_is_idle(void)
+{
+ char *p;
+ char buffer[20]; /* looking for "12345 (mbrola) S" so 20 is plenty*/
+
+ /* look in /proc to determine if mbrola is still running or sleeping */
+ if (lseek(mbr_proc_stat, 0, SEEK_SET) != 0)
+ return 0;
+ if (read(mbr_proc_stat, buffer, sizeof(buffer)) != sizeof(buffer))
+ return 0;
+ p = (char *)memchr(buffer, ')', sizeof(buffer));
+ if (!p || (unsigned)(p - buffer) >= sizeof(buffer) - 2)
+ return 0;
+ return (p[1] == ' ' && p[2] == 'S');
+}
+
+static ssize_t receive_from_mbrola(void *buffer, size_t bufsize)
+{
+ int result, wait = 1;
+ size_t cursize = 0;
+
+ if (!mbr_pid)
+ return -1;
+
+ do {
+ struct pollfd pollfd[3];
+ nfds_t nfds = 0;
+ int idle;
+
+ pollfd[0].fd = mbr_audio_fd;
+ pollfd[0].events = POLLIN;
+ nfds++;
+
+ pollfd[1].fd = mbr_error_fd;
+ pollfd[1].events = POLLIN;
+ nfds++;
+
+ if (mbr_pending_data_head) {
+ pollfd[2].fd = mbr_cmd_fd;
+ pollfd[2].events = POLLOUT;
+ nfds++;
+ }
+
+ idle = mbrola_is_idle();
+ result = poll(pollfd, nfds, idle ? 0 : wait);
+ if (result == -1) {
+ err("poll(): %s", strerror(errno));
+ return -1;
+ }
+ if (result == 0) {
+ if (idle) {
+ mbr_state = MBR_IDLE;
+ break;
+ } else {
+ if (wait >= 5000 * (4-1)/4) {
+ mbr_state = MBR_WEDGED;
+ err("mbrola process is stalled");
+ break;
+ } else {
+ wait *= 4;
+ continue;
+ }
+ }
+ }
+ wait = 1;
+
+ if (pollfd[1].revents && mbrola_has_errors())
+ return -1;
+
+ if (mbr_pending_data_head && pollfd[2].revents) {
+ struct datablock *head = mbr_pending_data_head;
+ char *data = head->buffer + head->done;
+ int left = head->size - head->done;
+ result = write(mbr_cmd_fd, data, left);
+ if (result == -1) {
+ int error = errno;
+ if (error == EPIPE && mbrola_has_errors())
+ return -1;
+ err("write(): %s", strerror(error));
+ return -1;
+ }
+ if (result != left) {
+ head->done += result;
+ } else {
+ mbr_pending_data_head = head->next;
+ free(head);
+ if (!mbr_pending_data_head)
+ mbr_pending_data_tail = NULL;
+ else
+ continue;
+ }
+ }
+
+ if (pollfd[0].revents) {
+ char *curpos = (char *)buffer + cursize;
+ size_t space = bufsize - cursize;
+ ssize_t obtained = read(mbr_audio_fd, curpos, space);
+ if (obtained == -1) {
+ err("read(): %s", strerror(errno));
+ return -1;
+ }
+ cursize += obtained;
+ mbr_state = MBR_AUDIO;
+ }
+ } while (cursize < bufsize);
+
+ return cursize;
+}
+
+/*
+ * API functions.
+ */
+
+int init_MBR(const char *voice_path)
+{
+ int error, result;
+ unsigned char wavhdr[45];
+
+ error = start_mbrola(voice_path);
+ if (error)
+ return -1;
+
+ result = send_to_mbrola("#\n");
+ if (result != 2) {
+ stop_mbrola();
+ return -1;
+ }
+
+ /* we should actually be getting only 44 bytes */
+ result = receive_from_mbrola(wavhdr, 45);
+ if (result != 44) {
+ if (result >= 0)
+ err("unable to get .wav header from mbrola");
+ stop_mbrola();
+ return -1;
+ }
+
+ /* parse wavhdr to get mbrola voice samplerate */
+ if (memcmp(wavhdr, "RIFF", 4) != 0 ||
+ memcmp(wavhdr+8, "WAVEfmt ", 8) != 0) {
+ err("mbrola did not return a .wav header");
+ stop_mbrola();
+ return -1;
+ }
+ mbr_samplerate = wavhdr[24] + (wavhdr[25]<<8) +
+ (wavhdr[26]<<16) + (wavhdr[27]<<24);
+ //mbro_log("mbrowrap: voice samplerate = %d", mbr_samplerate);
+
+ /* remember the voice path for setVolumeRatio_MBR() */
+ if (mbr_voice_path != voice_path) {
+ free(mbr_voice_path);
+ mbr_voice_path = strdup(voice_path);
+ }
+
+ return 0;
+}
+
+void close_MBR(void)
+{
+ stop_mbrola();
+ free_pending_data();
+ free(mbr_voice_path);
+ mbr_voice_path = NULL;
+ mbr_volume = 1.0;
+}
+
+int reset_MBR()
+{
+ int result, success = 1;
+ char dummybuf[4096];
+
+ if (mbr_state == MBR_IDLE)
+ return 1;
+ if (!mbr_pid)
+ return 0;
+ if (kill(mbr_pid, SIGUSR1) == -1)
+ success = 0;
+ free_pending_data();
+ result = write(mbr_cmd_fd, "\n#\n", 3);
+ if (result != 3)
+ success = 0;
+ do {
+ result = read(mbr_audio_fd, dummybuf, sizeof(dummybuf));
+ } while (result > 0);
+ if (result != -1 || errno != EAGAIN)
+ success = 0;
+ if (!mbrola_has_errors() && success)
+ mbr_state = MBR_IDLE;
+ return success;
+}
+
+int read_MBR(void *buffer, int nb_samples)
+{
+ int result = receive_from_mbrola(buffer, nb_samples * 2);
+ if (result > 0)
+ result /= 2;
+ return result;
+}
+
+int write_MBR(const char *data)
+{
+ mbr_state = MBR_NEWDATA;
+ return send_to_mbrola(data);
+}
+
+int flush_MBR(void)
+{
+ return send_to_mbrola("\n#\n") == 3;
+}
+
+int getFreq_MBR(void)
+{
+ return mbr_samplerate;
+}
+
+void setVolumeRatio_MBR(float value)
+{
+ if (value == mbr_volume)
+ return;
+ mbr_volume = value;
+ if (mbr_state != MBR_IDLE)
+ return;
+ /*
+ * We have no choice but to kill and restart mbrola with
+ * the new argument here.
+ */
+ stop_mbrola();
+ init_MBR(mbr_voice_path);
+}
+
+int lastErrorStr_MBR(char *buffer, int bufsize)
+{
+ int result;
+ if (mbr_pid)
+ mbrola_has_errors();
+ result = snprintf(buffer, bufsize, "%s", mbr_errorbuf);
+ return result >= bufsize ? (bufsize - 1) : result;
+}
+
+void resetError_MBR(void)
+{
+ mbr_errorbuf[0] = 0;
+}
+
+#endif // INCLUDE_MBROLA
diff --git a/navit/support/espeak/mbrowrap.h b/navit/support/espeak/mbrowrap.h
new file mode 100644
index 000000000..c55028cf4
--- /dev/null
+++ b/navit/support/espeak/mbrowrap.h
@@ -0,0 +1,108 @@
+/*
+ * mbrowrap -- A wrapper library around the mbrola binary
+ * providing a subset of the API from the Windows mbrola DLL.
+ *
+ * Copyright (C) 2010 by Nicolas Pitre <nico@fluxnic.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MBROWRAP_H
+#define MBROWRAP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*
+ * Initialize mbrola. The 'voice_path' argument must contain the
+ * path and file name to the mbrola voice database to be used. Returned
+ * value is 0 on success, or an error code otherwise (currently only -1
+ * is returned. If not successful, lastErrorStr_MBR() will provide the
+ * error reason. If this is successful, then close_MBR() must be called
+ * before init_MBR() can be called again.
+ */
+int init_MBR(const char *voice_path);
+
+/*
+ * Stop mbrola and release any resources. It is necessary to call
+ * this after a successful call to init_MBR() before init_MBR() can be
+ * called again.
+ */
+void close_MBR(void);
+
+/*
+ * Stop any ongoing processing and flush all buffers. After this call
+ * any synthesis request will start afresh. A non-zero value is returned
+ * on success, or 0 on failure. If not successful, lastErrorStr_MBR() will
+ * provide the error reason.
+ */
+int reset_MBR(void);
+
+/*
+ * Return at most 'nb_samples' audio samples into 'buffer'. The returned
+ * value is the actual number of samples returned, or -1 on error.
+ * If not successful, lastErrorStr_MBR() will provide the error reason.
+ * Samples are always 16-bit little endian.
+ */
+int read_MBR(void *buffer, int nb_samples);
+
+/*
+ * Write a NULL terminated string of phoneme in the input buffer.
+ * Return the number of chars actually written, or -1 on error.
+ * If not successful, lastErrorStr_MBR() will provide the error reason.
+ */
+int write_MBR(const char *data);
+
+/*
+ * Send a flush command to the mbrola input stream.
+ * This is currently similar to write_MBR("#\n"). Return 1 on success
+ * or 0 on failure. If not successful, lastErrorStr_MBR() will provide
+ * the error reason.
+ */
+int flush_MBR(void);
+
+/*
+ * Return the audio sample frequency of the used voice database.
+ */
+int getFreq_MBR(void);
+
+/*
+ * Overall volume.
+ */
+void setVolumeRatio_MBR(float value);
+
+/*
+ * Copy into 'buffer' at most 'bufsize' bytes from the latest error
+ * message. This may also contain non-fatal errors from mbrola. The
+ * returned value is the actual number of bytes copied. When no error
+ * message is pending then an empty string is returned. Consecutive
+ * calls to lastErrorStr_MBR() will return the same message unless it
+ * is explicitly cleared with resetError_MBR().
+ */
+int lastErrorStr_MBR(char *buffer, int bufsize);
+
+/*
+ * Clear any pending error message.
+ */
+void resetError_MBR(void);
+
+/*
+ * Tolerance to missing diphones (always active so this is ignored)
+ */
+static inline void setNoError_MBR(int no_error) { }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/navit/support/espeak/numbers.c b/navit/support/espeak/numbers.c
index 9c74eaca3..0289ab4ec 100644
--- a/navit/support/espeak/numbers.c
+++ b/navit/support/espeak/numbers.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <ctype.h>
+#include <stdbool.h> //for bool type
#include <stdlib.h>
#include <string.h>
@@ -65,37 +66,43 @@
#define M_MIDDLE_DOT M_DOT_ABOVE // duplicate of M_DOT_ABOVE
#define M_IMPLOSIVE M_HOOK
+static int n_digit_lookup;
+static char *digit_lookup;
+static int speak_missing_thousands;
+static int number_control;
+
+
typedef struct {
-const char *name;
-int flags;
+ const char *name;
+ int flags;
} ACCENTS;
// these are tokens to look up in the *_list file.
static ACCENTS accents_tab[] = {
-{"_lig", 1},
-{"_smc", 1}, // smallcap
-{"_tur", 1}, // turned
-{"_rev", 1}, // reversed
-{"_crl", 0}, // curl
-
-{"_acu", 0}, // acute
-{"_brv", 0}, // breve
-{"_hac", 0}, // caron/hacek
-{"_ced", 0}, // cedilla
-{"_cir", 0}, // circumflex
-{"_dia", 0}, // diaeresis
-{"_ac2", 0}, // double acute
-{"_dot", 0}, // dot
-{"_grv", 0}, // grave
-{"_mcn", 0}, // macron
-{"_ogo", 0}, // ogonek
-{"_rng", 0}, // ring
-{"_stk", 0}, // stroke
-{"_tld", 0}, // tilde
-
-{"_bar", 0}, // bar
-{"_rfx", 0}, // retroflex
-{"_hok", 0}, // hook
+ {"_lig", 1},
+ {"_smc", 1}, // smallcap
+ {"_tur", 1}, // turned
+ {"_rev", 1}, // reversed
+ {"_crl", 0}, // curl
+
+ {"_acu", 0}, // acute
+ {"_brv", 0}, // breve
+ {"_hac", 0}, // caron/hacek
+ {"_ced", 0}, // cedilla
+ {"_cir", 0}, // circumflex
+ {"_dia", 0}, // diaeresis
+ {"_ac2", 0}, // double acute
+ {"_dot", 0}, // dot
+ {"_grv", 0}, // grave
+ {"_mcn", 0}, // macron
+ {"_ogo", 0}, // ogonek
+ {"_rng", 0}, // ring
+ {"_stk", 0}, // stroke
+ {"_tld", 0}, // tilde
+
+ {"_bar", 0}, // bar
+ {"_rfx", 0}, // retroflex
+ {"_hok", 0}, // hook
};
@@ -121,270 +128,271 @@ static ACCENTS accents_tab[] = {
static const short non_ascii_tab[] = {
- 0, 0x3b1, 0x259, 0x25b, 0x3b3, 0x3b9, 0x153, 0x3c9,
-0x3c6, 0x283, 0x3c5, 0x292, 0x294, 0x27e };
+ 0, 0x3b1, 0x259, 0x25b, 0x3b3, 0x3b9, 0x153, 0x3c9,
+ 0x3c6, 0x283, 0x3c5, 0x292, 0x294, 0x27e
+};
// characters U+00e0 to U+017f
static const unsigned short letter_accents_0e0[] = {
-LETTER('a',M_GRAVE,0), // U+00e0
-LETTER('a',M_ACUTE,0),
-LETTER('a',M_CIRCUMFLEX,0),
-LETTER('a',M_TILDE,0),
-LETTER('a',M_DIAERESIS,0),
-LETTER('a',M_RING,0),
-LIGATURE('a','e',0),
-LETTER('c',M_CEDILLA,0),
-LETTER('e',M_GRAVE,0),
-LETTER('e',M_ACUTE,0),
-LETTER('e',M_CIRCUMFLEX,0),
-LETTER('e',M_DIAERESIS,0),
-LETTER('i',M_GRAVE,0),
-LETTER('i',M_ACUTE,0),
-LETTER('i',M_CIRCUMFLEX,0),
-LETTER('i',M_DIAERESIS,0),
-LETTER('d',M_NAME,0), // eth // U+00f0
-LETTER('n',M_TILDE,0),
-LETTER('o',M_GRAVE,0),
-LETTER('o',M_ACUTE,0),
-LETTER('o',M_CIRCUMFLEX,0),
-LETTER('o',M_TILDE,0),
-LETTER('o',M_DIAERESIS,0),
-0, // division sign
-LETTER('o',M_STROKE,0),
-LETTER('u',M_GRAVE,0),
-LETTER('u',M_ACUTE,0),
-LETTER('u',M_CIRCUMFLEX,0),
-LETTER('u',M_DIAERESIS,0),
-LETTER('y',M_ACUTE,0),
-LETTER('t',M_NAME,0), // thorn
-LETTER('y',M_DIAERESIS,0),
-CAPITAL, // U+0100
-LETTER('a',M_MACRON,0),
-CAPITAL,
-LETTER('a',M_BREVE,0),
-CAPITAL,
-LETTER('a',M_OGONEK,0),
-CAPITAL,
-LETTER('c',M_ACUTE,0),
-CAPITAL,
-LETTER('c',M_CIRCUMFLEX,0),
-CAPITAL,
-LETTER('c',M_DOT_ABOVE,0),
-CAPITAL,
-LETTER('c',M_CARON,0),
-CAPITAL,
-LETTER('d',M_CARON,0),
-CAPITAL, // U+0110
-LETTER('d',M_STROKE,0),
-CAPITAL,
-LETTER('e',M_MACRON,0),
-CAPITAL,
-LETTER('e',M_BREVE,0),
-CAPITAL,
-LETTER('e',M_DOT_ABOVE,0),
-CAPITAL,
-LETTER('e',M_OGONEK,0),
-CAPITAL,
-LETTER('e',M_CARON,0),
-CAPITAL,
-LETTER('g',M_CIRCUMFLEX,0),
-CAPITAL,
-LETTER('g',M_BREVE,0),
-CAPITAL, // U+0120
-LETTER('g',M_DOT_ABOVE,0),
-CAPITAL,
-LETTER('g',M_CEDILLA,0),
-CAPITAL,
-LETTER('h',M_CIRCUMFLEX,0),
-CAPITAL,
-LETTER('h',M_STROKE,0),
-CAPITAL,
-LETTER('i',M_TILDE,0),
-CAPITAL,
-LETTER('i',M_MACRON,0),
-CAPITAL,
-LETTER('i',M_BREVE,0),
-CAPITAL,
-LETTER('i',M_OGONEK,0),
-CAPITAL, // U+0130
-LETTER('i',M_NAME,0), // dotless i
-CAPITAL,
-LIGATURE('i','j',0),
-CAPITAL,
-LETTER('j',M_CIRCUMFLEX,0),
-CAPITAL,
-LETTER('k',M_CEDILLA,0),
-LETTER('k',M_NAME,0), // kra
-CAPITAL,
-LETTER('l',M_ACUTE,0),
-CAPITAL,
-LETTER('l',M_CEDILLA,0),
-CAPITAL,
-LETTER('l',M_CARON,0),
-CAPITAL,
-LETTER('l',M_MIDDLE_DOT,0), // U+0140
-CAPITAL,
-LETTER('l',M_STROKE,0),
-CAPITAL,
-LETTER('n',M_ACUTE,0),
-CAPITAL,
-LETTER('n',M_CEDILLA,0),
-CAPITAL,
-LETTER('n',M_CARON,0),
-LETTER('n',M_NAME,0), // apostrophe n
-CAPITAL,
-LETTER('n',M_NAME,0), // eng
-CAPITAL,
-LETTER('o',M_MACRON,0),
-CAPITAL,
-LETTER('o',M_BREVE,0),
-CAPITAL, // U+0150
-LETTER('o',M_DOUBLE_ACUTE,0),
-CAPITAL,
-LIGATURE('o','e',0),
-CAPITAL,
-LETTER('r',M_ACUTE,0),
-CAPITAL,
-LETTER('r',M_CEDILLA,0),
-CAPITAL,
-LETTER('r',M_CARON,0),
-CAPITAL,
-LETTER('s',M_ACUTE,0),
-CAPITAL,
-LETTER('s',M_CIRCUMFLEX,0),
-CAPITAL,
-LETTER('s',M_CEDILLA,0),
-CAPITAL, // U+0160
-LETTER('s',M_CARON,0),
-CAPITAL,
-LETTER('t',M_CEDILLA,0),
-CAPITAL,
-LETTER('t',M_CARON,0),
-CAPITAL,
-LETTER('t',M_STROKE,0),
-CAPITAL,
-LETTER('u',M_TILDE,0),
-CAPITAL,
-LETTER('u',M_MACRON,0),
-CAPITAL,
-LETTER('u',M_BREVE,0),
-CAPITAL,
-LETTER('u',M_RING,0),
-CAPITAL, // U+0170
-LETTER('u',M_DOUBLE_ACUTE,0),
-CAPITAL,
-LETTER('u',M_OGONEK,0),
-CAPITAL,
-LETTER('w',M_CIRCUMFLEX,0),
-CAPITAL,
-LETTER('y',M_CIRCUMFLEX,0),
-CAPITAL, // Y-DIAERESIS
-CAPITAL,
-LETTER('z',M_ACUTE,0),
-CAPITAL,
-LETTER('z',M_DOT_ABOVE,0),
-CAPITAL,
-LETTER('z',M_CARON,0),
-LETTER('s',M_NAME,0), // long-s // U+17f
+ LETTER('a',M_GRAVE,0), // U+00e0
+ LETTER('a',M_ACUTE,0),
+ LETTER('a',M_CIRCUMFLEX,0),
+ LETTER('a',M_TILDE,0),
+ LETTER('a',M_DIAERESIS,0),
+ LETTER('a',M_RING,0),
+ LIGATURE('a','e',0),
+ LETTER('c',M_CEDILLA,0),
+ LETTER('e',M_GRAVE,0),
+ LETTER('e',M_ACUTE,0),
+ LETTER('e',M_CIRCUMFLEX,0),
+ LETTER('e',M_DIAERESIS,0),
+ LETTER('i',M_GRAVE,0),
+ LETTER('i',M_ACUTE,0),
+ LETTER('i',M_CIRCUMFLEX,0),
+ LETTER('i',M_DIAERESIS,0),
+ LETTER('d',M_NAME,0), // eth // U+00f0
+ LETTER('n',M_TILDE,0),
+ LETTER('o',M_GRAVE,0),
+ LETTER('o',M_ACUTE,0),
+ LETTER('o',M_CIRCUMFLEX,0),
+ LETTER('o',M_TILDE,0),
+ LETTER('o',M_DIAERESIS,0),
+ 0, // division sign
+ LETTER('o',M_STROKE,0),
+ LETTER('u',M_GRAVE,0),
+ LETTER('u',M_ACUTE,0),
+ LETTER('u',M_CIRCUMFLEX,0),
+ LETTER('u',M_DIAERESIS,0),
+ LETTER('y',M_ACUTE,0),
+ LETTER('t',M_NAME,0), // thorn
+ LETTER('y',M_DIAERESIS,0),
+ CAPITAL, // U+0100
+ LETTER('a',M_MACRON,0),
+ CAPITAL,
+ LETTER('a',M_BREVE,0),
+ CAPITAL,
+ LETTER('a',M_OGONEK,0),
+ CAPITAL,
+ LETTER('c',M_ACUTE,0),
+ CAPITAL,
+ LETTER('c',M_CIRCUMFLEX,0),
+ CAPITAL,
+ LETTER('c',M_DOT_ABOVE,0),
+ CAPITAL,
+ LETTER('c',M_CARON,0),
+ CAPITAL,
+ LETTER('d',M_CARON,0),
+ CAPITAL, // U+0110
+ LETTER('d',M_STROKE,0),
+ CAPITAL,
+ LETTER('e',M_MACRON,0),
+ CAPITAL,
+ LETTER('e',M_BREVE,0),
+ CAPITAL,
+ LETTER('e',M_DOT_ABOVE,0),
+ CAPITAL,
+ LETTER('e',M_OGONEK,0),
+ CAPITAL,
+ LETTER('e',M_CARON,0),
+ CAPITAL,
+ LETTER('g',M_CIRCUMFLEX,0),
+ CAPITAL,
+ LETTER('g',M_BREVE,0),
+ CAPITAL, // U+0120
+ LETTER('g',M_DOT_ABOVE,0),
+ CAPITAL,
+ LETTER('g',M_CEDILLA,0),
+ CAPITAL,
+ LETTER('h',M_CIRCUMFLEX,0),
+ CAPITAL,
+ LETTER('h',M_STROKE,0),
+ CAPITAL,
+ LETTER('i',M_TILDE,0),
+ CAPITAL,
+ LETTER('i',M_MACRON,0),
+ CAPITAL,
+ LETTER('i',M_BREVE,0),
+ CAPITAL,
+ LETTER('i',M_OGONEK,0),
+ CAPITAL, // U+0130
+ LETTER('i',M_NAME,0), // dotless i
+ CAPITAL,
+ LIGATURE('i','j',0),
+ CAPITAL,
+ LETTER('j',M_CIRCUMFLEX,0),
+ CAPITAL,
+ LETTER('k',M_CEDILLA,0),
+ LETTER('k',M_NAME,0), // kra
+ CAPITAL,
+ LETTER('l',M_ACUTE,0),
+ CAPITAL,
+ LETTER('l',M_CEDILLA,0),
+ CAPITAL,
+ LETTER('l',M_CARON,0),
+ CAPITAL,
+ LETTER('l',M_MIDDLE_DOT,0), // U+0140
+ CAPITAL,
+ LETTER('l',M_STROKE,0),
+ CAPITAL,
+ LETTER('n',M_ACUTE,0),
+ CAPITAL,
+ LETTER('n',M_CEDILLA,0),
+ CAPITAL,
+ LETTER('n',M_CARON,0),
+ LETTER('n',M_NAME,0), // apostrophe n
+ CAPITAL,
+ LETTER('n',M_NAME,0), // eng
+ CAPITAL,
+ LETTER('o',M_MACRON,0),
+ CAPITAL,
+ LETTER('o',M_BREVE,0),
+ CAPITAL, // U+0150
+ LETTER('o',M_DOUBLE_ACUTE,0),
+ CAPITAL,
+ LIGATURE('o','e',0),
+ CAPITAL,
+ LETTER('r',M_ACUTE,0),
+ CAPITAL,
+ LETTER('r',M_CEDILLA,0),
+ CAPITAL,
+ LETTER('r',M_CARON,0),
+ CAPITAL,
+ LETTER('s',M_ACUTE,0),
+ CAPITAL,
+ LETTER('s',M_CIRCUMFLEX,0),
+ CAPITAL,
+ LETTER('s',M_CEDILLA,0),
+ CAPITAL, // U+0160
+ LETTER('s',M_CARON,0),
+ CAPITAL,
+ LETTER('t',M_CEDILLA,0),
+ CAPITAL,
+ LETTER('t',M_CARON,0),
+ CAPITAL,
+ LETTER('t',M_STROKE,0),
+ CAPITAL,
+ LETTER('u',M_TILDE,0),
+ CAPITAL,
+ LETTER('u',M_MACRON,0),
+ CAPITAL,
+ LETTER('u',M_BREVE,0),
+ CAPITAL,
+ LETTER('u',M_RING,0),
+ CAPITAL, // U+0170
+ LETTER('u',M_DOUBLE_ACUTE,0),
+ CAPITAL,
+ LETTER('u',M_OGONEK,0),
+ CAPITAL,
+ LETTER('w',M_CIRCUMFLEX,0),
+ CAPITAL,
+ LETTER('y',M_CIRCUMFLEX,0),
+ CAPITAL, // Y-DIAERESIS
+ CAPITAL,
+ LETTER('z',M_ACUTE,0),
+ CAPITAL,
+ LETTER('z',M_DOT_ABOVE,0),
+ CAPITAL,
+ LETTER('z',M_CARON,0),
+ LETTER('s',M_NAME,0), // long-s // U+17f
};
// characters U+0250 to U+029F
static const unsigned short letter_accents_250[] = {
-LETTER('a',M_TURNED,0), // U+250
-LETTER(L_ALPHA,0,0),
-LETTER(L_ALPHA,M_TURNED,0),
-LETTER('b',M_IMPLOSIVE,0),
-0, // open-o
-LETTER('c',M_CURL,0),
-LETTER('d',M_RETROFLEX,0),
-LETTER('d',M_IMPLOSIVE,0),
-LETTER('e',M_REVERSED,0), // U+258
-0, // schwa
-LETTER(L_SCHWA,M_HOOK,0),
-0, // open-e
-LETTER(L_OPEN_E,M_REVERSED,0),
-LETTER(L_OPEN_E,M_HOOK,M_REVERSED),
-0,//LETTER(L_OPEN_E,M_CLOSED,M_REVERSED),
-LETTER('j',M_BAR,0),
-LETTER('g',M_IMPLOSIVE,0), // U+260
-LETTER('g',0,0),
-LETTER('g',M_SMALLCAP,0),
-LETTER(L_GAMMA,0,0),
-0, // ramshorn
-LETTER('h',M_TURNED,0),
-LETTER('h',M_HOOK,0),
-0,//LETTER(L_HENG,M_HOOK,0),
-LETTER('i',M_BAR,0), // U+268
-LETTER(L_IOTA,0,0),
-LETTER('i',M_SMALLCAP,0),
-LETTER('l',M_TILDE,0),
-LETTER('l',M_BAR,0),
-LETTER('l',M_RETROFLEX,0),
-LIGATURE('l','z',0),
-LETTER('m',M_TURNED,0),
-0,//LETTER('m',M_TURNED,M_LEG), // U+270
-LETTER('m',M_HOOK,0),
-0,//LETTER('n',M_LEFTHOOK,0),
-LETTER('n',M_RETROFLEX,0),
-LETTER('n',M_SMALLCAP,0),
-LETTER('o',M_BAR,0),
-LIGATURE('o','e',M_SMALLCAP),
-0,//LETTER(L_OMEGA,M_CLOSED,0),
-LETTER(L_PHI,0,0), // U+278
-LETTER('r',M_TURNED,0),
-0,//LETTER('r',M_TURNED,M_LEG),
-LETTER('r',M_RETROFLEX,M_TURNED),
-0,//LETTER('r',M_LEG,0),
-LETTER('r',M_RETROFLEX,0),
-0, // r-tap
-LETTER(L_RTAP,M_REVERSED,0),
-LETTER('r',M_SMALLCAP,0), // U+280
-LETTER('r',M_TURNED,M_SMALLCAP),
-LETTER('s',M_RETROFLEX,0),
-0, // esh
-0,//LETTER('j',M_BAR,L_IMPLOSIVE),
-LETTER(L_ESH,M_REVERSED,0),
-LETTER(L_ESH,M_CURL,0),
-LETTER('t',M_TURNED,0),
-LETTER('t',M_RETROFLEX,0), // U+288
-LETTER('u',M_BAR,0),
-LETTER(L_UPSILON,0,0),
-LETTER('v',M_HOOK,0),
-LETTER('v',M_TURNED,0),
-LETTER('w',M_TURNED,0),
-LETTER('y',M_TURNED,0),
-LETTER('y',M_SMALLCAP,0),
-LETTER('z',M_RETROFLEX,0), // U+290
-LETTER('z',M_CURL,0),
-0, // ezh
-LETTER(L_EZH,M_CURL,0),
-0, // glottal stop
-LETTER(L_GLOTTAL,M_REVERSED,0),
-LETTER(L_GLOTTAL,M_TURNED,0),
-0,//LETTER('c',M_LONG,0),
-0, // bilabial click // U+298
-LETTER('b',M_SMALLCAP,0),
-0,//LETTER(L_OPEN_E,M_CLOSED,0),
-LETTER('g',M_IMPLOSIVE,M_SMALLCAP),
-LETTER('h',M_SMALLCAP,0),
-LETTER('j',M_CURL,0),
-LETTER('k',M_TURNED,0),
-LETTER('l',M_SMALLCAP,0),
-LETTER('q',M_HOOK,0), // U+2a0
-LETTER(L_GLOTTAL,M_STROKE,0),
-LETTER(L_GLOTTAL,M_STROKE,M_REVERSED),
-LIGATURE('d','z',0),
-0, // dezh
-LIGATURE('d','z',M_CURL),
-LIGATURE('t','s',0),
-0, // tesh
-LIGATURE('t','s',M_CURL),
+ LETTER('a',M_TURNED,0), // U+250
+ LETTER(L_ALPHA,0,0),
+ LETTER(L_ALPHA,M_TURNED,0),
+ LETTER('b',M_IMPLOSIVE,0),
+ 0, // open-o
+ LETTER('c',M_CURL,0),
+ LETTER('d',M_RETROFLEX,0),
+ LETTER('d',M_IMPLOSIVE,0),
+ LETTER('e',M_REVERSED,0), // U+258
+ 0, // schwa
+ LETTER(L_SCHWA,M_HOOK,0),
+ 0, // open-e
+ LETTER(L_OPEN_E,M_REVERSED,0),
+ LETTER(L_OPEN_E,M_HOOK,M_REVERSED),
+ 0,//LETTER(L_OPEN_E,M_CLOSED,M_REVERSED),
+ LETTER('j',M_BAR,0),
+ LETTER('g',M_IMPLOSIVE,0), // U+260
+ LETTER('g',0,0),
+ LETTER('g',M_SMALLCAP,0),
+ LETTER(L_GAMMA,0,0),
+ 0, // ramshorn
+ LETTER('h',M_TURNED,0),
+ LETTER('h',M_HOOK,0),
+ 0,//LETTER(L_HENG,M_HOOK,0),
+ LETTER('i',M_BAR,0), // U+268
+ LETTER(L_IOTA,0,0),
+ LETTER('i',M_SMALLCAP,0),
+ LETTER('l',M_TILDE,0),
+ LETTER('l',M_BAR,0),
+ LETTER('l',M_RETROFLEX,0),
+ LIGATURE('l','z',0),
+ LETTER('m',M_TURNED,0),
+ 0,//LETTER('m',M_TURNED,M_LEG), // U+270
+ LETTER('m',M_HOOK,0),
+ 0,//LETTER('n',M_LEFTHOOK,0),
+ LETTER('n',M_RETROFLEX,0),
+ LETTER('n',M_SMALLCAP,0),
+ LETTER('o',M_BAR,0),
+ LIGATURE('o','e',M_SMALLCAP),
+ 0,//LETTER(L_OMEGA,M_CLOSED,0),
+ LETTER(L_PHI,0,0), // U+278
+ LETTER('r',M_TURNED,0),
+ 0,//LETTER('r',M_TURNED,M_LEG),
+ LETTER('r',M_RETROFLEX,M_TURNED),
+ 0,//LETTER('r',M_LEG,0),
+ LETTER('r',M_RETROFLEX,0),
+ 0, // r-tap
+ LETTER(L_RTAP,M_REVERSED,0),
+ LETTER('r',M_SMALLCAP,0), // U+280
+ LETTER('r',M_TURNED,M_SMALLCAP),
+ LETTER('s',M_RETROFLEX,0),
+ 0, // esh
+ 0,//LETTER('j',M_BAR,L_IMPLOSIVE),
+ LETTER(L_ESH,M_REVERSED,0),
+ LETTER(L_ESH,M_CURL,0),
+ LETTER('t',M_TURNED,0),
+ LETTER('t',M_RETROFLEX,0), // U+288
+ LETTER('u',M_BAR,0),
+ LETTER(L_UPSILON,0,0),
+ LETTER('v',M_HOOK,0),
+ LETTER('v',M_TURNED,0),
+ LETTER('w',M_TURNED,0),
+ LETTER('y',M_TURNED,0),
+ LETTER('y',M_SMALLCAP,0),
+ LETTER('z',M_RETROFLEX,0), // U+290
+ LETTER('z',M_CURL,0),
+ 0, // ezh
+ LETTER(L_EZH,M_CURL,0),
+ 0, // glottal stop
+ LETTER(L_GLOTTAL,M_REVERSED,0),
+ LETTER(L_GLOTTAL,M_TURNED,0),
+ 0,//LETTER('c',M_LONG,0),
+ 0, // bilabial click // U+298
+ LETTER('b',M_SMALLCAP,0),
+ 0,//LETTER(L_OPEN_E,M_CLOSED,0),
+ LETTER('g',M_IMPLOSIVE,M_SMALLCAP),
+ LETTER('h',M_SMALLCAP,0),
+ LETTER('j',M_CURL,0),
+ LETTER('k',M_TURNED,0),
+ LETTER('l',M_SMALLCAP,0),
+ LETTER('q',M_HOOK,0), // U+2a0
+ LETTER(L_GLOTTAL,M_STROKE,0),
+ LETTER(L_GLOTTAL,M_STROKE,M_REVERSED),
+ LIGATURE('d','z',0),
+ 0, // dezh
+ LIGATURE('d','z',M_CURL),
+ LIGATURE('t','s',0),
+ 0, // tesh
+ LIGATURE('t','s',M_CURL),
};
static int LookupLetter2(Translator *tr, unsigned int letter, char *ph_buf)
-{//========================================================================
+{ //========================================================================
int len;
char single_letter[10];
@@ -425,11 +433,10 @@ void LookupAccentedLetter(Translator *tr, unsigned int letter, char *ph_buf)
{
accent_data = letter_accents_0e0[letter - 0xe0];
}
- else
- if((letter >= 0x250) && (letter <= 0x2a8))
+ else if((letter >= 0x250) && (letter <= 0x2a8))
{
accent_data = letter_accents_250[letter - 0x250];
- }
+ }
if(accent_data != 0)
{
@@ -479,8 +486,7 @@ void LookupAccentedLetter(Translator *tr, unsigned int letter, char *ph_buf)
{
if(accent1 == 0)
strcpy(ph_buf, ph_letter1);
- else
- if((tr->langopts.accents & 1) || (accents_tab[accent1].flags & 1))
+ else if((tr->langopts.accents & 1) || (accents_tab[accent1].flags & 1))
sprintf(ph_buf,"%s%c%c%s", ph_accent1, phonPAUSE_VSHORT, phonSTRESS_P, ph_letter1);
else
sprintf(ph_buf,"%c%s%c%s%c", phonSTRESS_2, ph_letter1, phonPAUSE_VSHORT, ph_accent1, phonPAUSE_VSHORT);
@@ -492,15 +498,14 @@ void LookupAccentedLetter(Translator *tr, unsigned int letter, char *ph_buf)
-void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf1)
-{//=================================================================================
+void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf1, int control)
+{//==============================================================================================
+// control, bit 0: not the first letter of a word
+
int len;
- unsigned char *p;
static char single_letter[10] = {0,0};
- char ph_stress[2];
unsigned int dict_flags[2];
char ph_buf3[40];
- char *ptr;
ph_buf1[0] = 0;
len = utf8_out(letter,&single_letter[2]);
@@ -540,14 +545,13 @@ void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_b
if(next_byte != ' ')
next_byte = RULE_SPELLING;
- single_letter[3+len] = next_byte; // follow by space-space if the end of the word, or space-0x31
+ single_letter[3+len] = next_byte; // follow by space-space if the end of the word, or space-31
single_letter[1] = '_';
// if the $accent flag is set for this letter, use the accents table (below)
dict_flags[1] = 0;
- ptr = &single_letter[1];
-
+
if(Lookup(tr, &single_letter[1], ph_buf3) == 0)
{
single_letter[1] = ' ';
@@ -562,48 +566,170 @@ void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_b
LookupAccentedLetter(tr, letter, ph_buf3);
}
- if(ph_buf3[0] == 0)
+ strcpy(ph_buf1, ph_buf3);
+ if((ph_buf1[0] == 0) || (ph_buf1[0] == phonSWITCH))
{
- ph_buf1[0] = 0;
return;
}
- if(ph_buf3[0] == phonSWITCH)
+
+ dict_flags[0] = 0;
+ dict_flags[1] = 0;
+ SetWordStress(tr, ph_buf1, dict_flags, -1, control & 1);
+
+} // end of LookupLetter
+
+
+// unicode ranges for non-ascii digits 0-9
+static const int number_ranges[] = {
+ 0x660, 0x6f0, // arabic
+ 0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xbe6, 0xc66, 0xce6, 0xd66, // indic
+ 0xe50, 0xed0, 0xf20, 0x1040, 0x1090,
+ 0 }; // these must be in ascending order
+
+
+static int NonAsciiNumber(int letter)
+{//============================
+// Change non-ascii digit into ascii digit '0' to '9', (or -1 if not)
+ const int *p;
+ int base;
+
+ for(p=number_ranges; (base = *p) != 0; p++)
{
- strcpy(ph_buf1,ph_buf3);
- return;
+ if(letter < base)
+ break; // not found
+ if(letter < (base+10))
+ return(letter-base+'0');
}
- // at a stress marker at the start of the letter name, unless one is already marked
- ph_stress[0] = phonSTRESS_P;
- ph_stress[1] = 0;
+ return(-1);
+}
+
+#define L_SUB 0x4000 // subscript
+#define L_SUP 0x8000 // superscript
+
+static const char *modifiers[] = {NULL, "_sub", "_sup", NULL};
+
+// this list must be in ascending order
+static unsigned short derived_letters[] = {
+ 0x00aa, 'a'+L_SUP,
+ 0x00b2, '2'+L_SUP,
+ 0x00b3, '3'+L_SUP,
+ 0x00b9, '1'+L_SUP,
+ 0x00ba, 'o'+L_SUP,
+ 0x02b0, 'h'+L_SUP,
+ 0x02b1, 0x266+L_SUP,
+ 0x02b2, 'j'+L_SUP,
+ 0x02b3, 'r'+L_SUP,
+ 0x02b4, 0x279+L_SUP,
+ 0x02b5, 0x27b+L_SUP,
+ 0x02b6, 0x281+L_SUP,
+ 0x02b7, 'w'+L_SUP,
+ 0x02b8, 'y'+L_SUP,
+ 0x02c0, 0x294+L_SUP,
+ 0x02c1, 0x295+L_SUP,
+ 0x02e0, 0x263+L_SUP,
+ 0x02e1, 'l'+L_SUP,
+ 0x02e2, 's'+L_SUP,
+ 0x02e3, 'x'+L_SUP,
+ 0x2070, '0'+L_SUP,
+ 0x2071, 'i'+L_SUP,
+ 0x2074, '4'+L_SUP,
+ 0x2075, '5'+L_SUP,
+ 0x2076, '6'+L_SUP,
+ 0x2077, '7'+L_SUP,
+ 0x2078, '8'+L_SUP,
+ 0x2079, '9'+L_SUP,
+ 0x207a, '+'+L_SUP,
+ 0x207b, '-'+L_SUP,
+ 0x207c, '='+L_SUP,
+ 0x207d, '('+L_SUP,
+ 0x207e, ')'+L_SUP,
+ 0x207f, 'n'+L_SUP,
+ 0x2080, '0'+L_SUB,
+ 0x2081, '1'+L_SUB,
+ 0x2082, '2'+L_SUB,
+ 0x2083, '3'+L_SUB,
+ 0x2084, '4'+L_SUB,
+ 0x2085, '5'+L_SUB,
+ 0x2086, '6'+L_SUB,
+ 0x2087, '7'+L_SUB,
+ 0x2088, '8'+L_SUB,
+ 0x2089, '9'+L_SUB,
+ 0x208a, '+'+L_SUB,
+ 0x208b, '-'+L_SUB,
+ 0x208c, '='+L_SUB,
+ 0x208d, '('+L_SUB,
+ 0x208e, ')'+L_SUB,
+ 0x2090, 'a'+L_SUB,
+ 0x2091, 'e'+L_SUB,
+ 0x2092, 'o'+L_SUB,
+ 0x2093, 'x'+L_SUB,
+ 0x2094, 0x259+L_SUB,
+ 0x2095, 'h'+L_SUB,
+ 0x2096, 'k'+L_SUB,
+ 0x2097, 'l'+L_SUB,
+ 0x2098, 'm'+L_SUB,
+ 0x2099, 'n'+L_SUB,
+ 0x209a, 'p'+L_SUB,
+ 0x209b, 's'+L_SUB,
+ 0x209c, 't'+L_SUB,
+ 0,0};
+
+
+static const char *hex_letters[] = {"'e:j","b'i:","s'i:","d'i:","'i:","'ef"}; // names, using phonemes available to all languages
+
+
+int IsSuperscript(int letter)
+{//===========================
+// is this a subscript or superscript letter ?
+ int ix;
+ int c;
- for(p=(unsigned char *)ph_buf3; *p != 0; p++)
+ for(ix=0; (c = derived_letters[ix]) != 0; ix+=2)
{
- if(phoneme_tab[*p]->type == phSTRESS)
- ph_stress[0] = 0; // stress is already marked
+ if(c > letter)
+ break;
+ if(c == letter)
+ return(derived_letters[ix+1]);
}
- sprintf(ph_buf1,"%s%s",ph_stress,ph_buf3);
+ return(0);
}
-int TranslateLetter(Translator *tr, char *word, char *phonemes, int control, int word_length)
-{//======================================================================================
+int TranslateLetter(Translator *tr, char *word, char *phonemes, int control)
+{//=========================================================================
// get pronunciation for an isolated letter
// return number of bytes used by the letter
-// control 2=say-as glyphs, 3-say-as chars
+// control bit 0: a non-initial letter in a word
+// bit 1: say 'capital'
+// bit 2: say character code for unknown letters
+
int n_bytes;
int letter;
int len;
- int save_option_phonemes;
+ int ix;
+ int c;
char *p2;
char *pbuf;
- char capital[20];
- char ph_buf[60];
- char ph_buf2[60];
- char hexbuf[6];
+ const char *modifier;
+ ALPHABET *alphabet;
+ int al_offset;
+ int al_flags;
+ int language;
+ int number;
+ int phontab_1;
+ int speak_letter_number;
+ char capital[30];
+ char ph_buf[80];
+ char ph_buf2[80];
+ char ph_alphabet[80];
+ char hexbuf[12];
+ static char pause_string[] = {phonPAUSE, 0};
ph_buf[0] = 0;
+ ph_alphabet[0] = 0;
capital[0] = 0;
+ phontab_1 = translator->phoneme_tab_ix;
n_bytes = utf8_in(&letter,word);
@@ -612,17 +738,45 @@ int TranslateLetter(Translator *tr, char *word, char *phonemes, int control, int
letter &= 0xff; // uncode private usage area
}
- if(control > 2)
+ if(control & 2)
{
// include CAPITAL information
- if(iswupper(letter))
+ if(iswupper2(letter))
{
Lookup(tr, "_cap", capital);
}
}
letter = towlower2(letter);
+ LookupLetter(tr, letter, word[n_bytes], ph_buf, control & 1);
- LookupLetter(tr, letter, word[n_bytes], ph_buf);
+ if(ph_buf[0] == 0)
+ {
+ // is this a subscript or superscript letter ?
+ if((c = IsSuperscript(letter)) != 0)
+ {
+ letter = c & 0x3fff;
+ if((control & 4 ) && ((modifier = modifiers[c >> 14]) != NULL))
+ {
+ // don't say "superscript" during normal text reading
+ Lookup(tr, modifier, capital);
+ if(capital[0] == 0)
+ {
+ capital[2] = SetTranslator2("en"); // overwrites previous contents of translator2
+ Lookup(translator2, modifier, &capital[3]);
+ if(capital[3] != 0)
+ {
+ capital[0] = phonPAUSE;
+ capital[1] = phonSWITCH;
+ len = strlen(&capital[3]);
+ capital[len+3] = phonSWITCH;
+ capital[len+4] = phontab_1;
+ capital[len+5] = 0;
+ }
+ }
+ }
+ }
+ LookupLetter(tr, letter, word[n_bytes], ph_buf, control & 1);
+ }
if(ph_buf[0] == phonSWITCH)
{
@@ -630,51 +784,206 @@ int TranslateLetter(Translator *tr, char *word, char *phonemes, int control, int
return(0);
}
- if((ph_buf[0] == 0) && (tr->translator_name != L('e','n')))
+
+ if((ph_buf[0] == 0) && ((number = NonAsciiNumber(letter)) > 0))
{
- // speak as English, check whether there is a translation for this character
- SetTranslator2("en");
- save_option_phonemes = option_phonemes;
- option_phonemes = 0;
- LookupLetter(translator2, letter, word[n_bytes], ph_buf);
- SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
- option_phonemes = save_option_phonemes;
+ // convert a non-ascii number to 0-9
+ LookupLetter(tr, number, 0, ph_buf, control & 1);
+ }
+
+ al_offset = 0;
+ al_flags = 0;
+ if((alphabet = AlphabetFromChar(letter)) != NULL)
+ {
+ al_offset = alphabet->offset;
+ al_flags = alphabet->flags;
+ }
- if(ph_buf[0] != 0)
+ if(alphabet != current_alphabet)
+ {
+ // speak the name of the alphabet
+ current_alphabet = alphabet;
+ if((alphabet != NULL) && !(al_flags & AL_DONT_NAME) && (al_offset != translator->letter_bits_offset))
{
- sprintf(phonemes,"%cen",phonSWITCH);
- return(0);
+ if((al_flags & AL_DONT_NAME) || (al_offset == translator->langopts.alt_alphabet) || (al_offset == translator->langopts.our_alphabet))
+ {
+ // don't say the alphabet name
+ }
+ else
+ {
+ ph_buf2[0] = 0;
+ if(Lookup(translator, alphabet->name, ph_alphabet) == 0) // the original language for the current voice
+ {
+ // Can't find the local name for this alphabet, use the English name
+ ph_alphabet[2] = SetTranslator2("en"); // overwrites previous contents of translator2
+ Lookup(translator2, alphabet->name, ph_buf2);
+ }
+ else if(translator != tr)
+ {
+ phontab_1 = tr->phoneme_tab_ix;
+ strcpy(ph_buf2, ph_alphabet);
+ ph_alphabet[2] = translator->phoneme_tab_ix;
+ }
+
+ if(ph_buf2[0] != 0)
+ {
+ // we used a different language for the alphabet name (now in ph_buf2)
+ ph_alphabet[0] = phonPAUSE;
+ ph_alphabet[1] = phonSWITCH;
+ strcpy(&ph_alphabet[3], ph_buf2);
+ len = strlen(ph_buf2) + 3;
+ ph_alphabet[len] = phonSWITCH;
+ ph_alphabet[len+1] = phontab_1;
+ ph_alphabet[len+2] = 0;
+ }
+ }
}
}
+
+// caution: SetWordStress() etc don't expect phonSWITCH + phoneme table number
+
if(ph_buf[0] == 0)
{
- // character name not found
- if(iswalpha(letter))
- Lookup(tr, "_?A", ph_buf);
+ if((al_offset != 0) && (al_offset == translator->langopts.alt_alphabet))
+ language = translator->langopts.alt_alphabet_lang;
+ else
+ if((alphabet != NULL) && (alphabet->language != 0) && !(al_flags & AL_NOT_LETTERS))
+ language = alphabet->language;
+ else
+ language = L('e','n');
- if((ph_buf[0]==0) && !iswspace(letter))
- Lookup(tr, "_??", ph_buf);
+ if((language != tr->translator_name) || (language == L('k','o')))
+ {
+ char *p3;
+ int initial, code;
+ char hangul_buf[12];
+
+ // speak in the language for this alphabet (or English)
+ ph_buf[2] = SetTranslator2(WordToString2(language));
+
+ if(translator2 != NULL)
+ {
+ if(((code = letter - 0xac00) >= 0) && (letter <= 0xd7af))
+ {
+ // Special case for Korean letters.
+ // break a syllable hangul into 2 or 3 individual jamo
+
+ hangul_buf[0] = ' ';
+ p3 = &hangul_buf[1];
+ if((initial = (code/28)/21) != 11)
+ {
+ p3 += utf8_out(initial + 0x1100, p3);
+ }
+ utf8_out(((code/28) % 21) + 0x1161, p3); // medial
+ utf8_out((code % 28) + 0x11a7, &p3[3]); // final
+ p3[6] = ' ';
+ p3[7] = 0;
+ ph_buf[3] = 0;
+ TranslateRules(translator2, &hangul_buf[1], &ph_buf[3], sizeof(ph_buf)-3, NULL, 0, NULL);
+ SetWordStress(translator2, &ph_buf[3], NULL, -1, 0);
+ }
+ else
+ {
+ LookupLetter(translator2, letter, word[n_bytes], &ph_buf[3], control & 1);
+ }
+
+ if(ph_buf[3] == phonSWITCH)
+ {
+ // another level of language change
+ ph_buf[2] = SetTranslator2(&ph_buf[4]);
+ LookupLetter(translator2, letter, word[n_bytes], &ph_buf[3], control & 1);
+ }
+
+ SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
- if(ph_buf[0] != 0)
+ if(ph_buf[3] != 0)
+ {
+ ph_buf[0] = phonPAUSE;
+ ph_buf[1] = phonSWITCH;
+ len = strlen(&ph_buf[3]) + 3;
+ ph_buf[len] = phonSWITCH; // switch back
+ ph_buf[len+1] = tr->phoneme_tab_ix;
+ ph_buf[len+2] = 0;
+ }
+ }
+ }
+ }
+
+ if(ph_buf[0] == 0)
+ {
+ // character name not found
+
+ if(ph_buf[0]== 0)
{
- // speak the hexadecimal number of the character code
- sprintf(hexbuf,"%x",letter);
- pbuf = ph_buf;
- for(p2 = hexbuf; *p2 != 0; p2++)
+ speak_letter_number = 1;
+ if(!(al_flags & AL_NO_SYMBOL))
+ {
+ if(iswalpha2(letter))
+ Lookup(translator, "_?A", ph_buf);
+
+ if((ph_buf[0]==0) && !iswspace(letter))
+ Lookup(translator, "_??", ph_buf);
+
+ if(ph_buf[0] == 0)
+ {
+ EncodePhonemes("l'et@", ph_buf, NULL);
+ }
+ }
+
+ if(!(control & 4) && (al_flags & AL_NOT_CODE))
+ {
+ // don't speak the character code number, unless we want full details of this character
+ speak_letter_number = 0;
+ }
+
+// if((ph_alphabet[0] != 0) && speak_letter_number)
+// ph_buf[0] = 0; // don't speak "letter" if we speak alphabet name
+
+ if(speak_letter_number)
{
- pbuf += strlen(pbuf);
- *pbuf++ = phonPAUSE_VSHORT;
- LookupLetter(tr, *p2, 0, pbuf);
+ if(al_offset == 0x2800)
+ {
+ // braille dots symbol, list the numbered dots
+ p2 = hexbuf;
+ for(ix=0; ix<8; ix++)
+ {
+ if(letter & (1 << ix))
+ {
+ *p2++ = '1'+ix;
+ }
+ }
+ *p2 = 0;
+ }
+ else
+ {
+ // speak the hexadecimal number of the character code
+ sprintf(hexbuf,"%x",letter);
+ }
+
+ pbuf = ph_buf;
+ for(p2 = hexbuf; *p2 != 0; p2++)
+ {
+ pbuf += strlen(pbuf);
+ *pbuf++ = phonPAUSE_VSHORT;
+ LookupLetter(translator, *p2, 0, pbuf, 1);
+ if(((pbuf[0] == 0) || (pbuf[0]==phonSWITCH)) && (*p2 >= 'a'))
+ {
+ // This language has no translation for 'a' to 'f', speak English names using base phonemes
+ EncodePhonemes(hex_letters[*p2 - 'a'], pbuf, NULL);
+ }
+ }
+ strcat(pbuf, pause_string);
}
}
}
len = strlen(phonemes);
- if(tr->langopts.accents & 2)
- sprintf(ph_buf2,"%c%s%s",0xff,ph_buf,capital);
+
+ if(tr->langopts.accents & 2) // 'capital' before or after the word ?
+ sprintf(ph_buf2,"%c%s%s%s",0xff,ph_alphabet,ph_buf,capital);
else
- sprintf(ph_buf2,"%c%s%s",0xff,capital,ph_buf); // the 0xff marker will be removed or replaced in SetSpellingStress()
+ sprintf(ph_buf2,"%c%s%s%s",0xff,ph_alphabet,capital,ph_buf); // the 0xff marker will be removed or replaced in SetSpellingStress()
if((len + strlen(ph_buf2)) < N_WORD_PHONEMES)
{
strcpy(&phonemes[len],ph_buf2);
@@ -690,23 +999,25 @@ void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars)
int ix;
unsigned int c;
int n_stress=0;
+ int prev = 0;
int count;
unsigned char buf[N_WORD_PHONEMES];
for(ix=0; (c = phonemes[ix]) != 0; ix++)
{
- if(c == phonSTRESS_P)
+ if((c == phonSTRESS_P) && (prev != phonSWITCH))
{
n_stress++;
}
- buf[ix] = c;
+ buf[ix] = prev = c;
}
buf[ix] = 0;
count = 0;
+ prev = 0;
for(ix=0; (c = buf[ix]) != 0; ix++)
{
- if((c == phonSTRESS_P) && (n_chars > 1))
+ if((c == phonSTRESS_P) && (n_chars > 1) && (prev != phonSWITCH))
{
count++;
@@ -725,8 +1036,7 @@ void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars)
}
}
}
- else
- if(c == 0xff)
+ else if(c == 0xff)
{
if((control < 2) || (ix==0))
continue; // don't insert pauses
@@ -734,11 +1044,11 @@ void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars)
if(control == 4)
c = phonPAUSE; // pause after each character
if(((count % 3) == 0) || (control > 2))
- c = phonPAUSE_SHORT; // pause following a primary stress
+ c = phonPAUSE_NOLINK; // pause following a primary stress
else
- continue; // remove marker
+ c = phonPAUSE_VSHORT;
}
- *phonemes++ = c;
+ *phonemes++ = prev = c;
}
if(control >= 2)
*phonemes++ = phonPAUSE_NOLINK;
@@ -750,9 +1060,86 @@ void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars)
// Numbers
static char ph_ordinal2[12];
+static char ph_ordinal2x[12];
+
+
+static int CheckDotOrdinal(Translator *tr, char *word, char *word_end, WORD_TAB *wtab, int roman)
+{//==============================================================================================
+
+ int ordinal = 0;
+ int c2;
+ int nextflags;
+
+ if((tr->langopts.numbers & NUM_ORDINAL_DOT) && ((word_end[0] == '.') || (wtab[0].flags & FLAG_HAS_DOT)) && !(wtab[1].flags & FLAG_NOSPACE))
+ {
+ if(roman || !(wtab[1].flags & FLAG_FIRST_UPPER))
+ {
+ if(word_end[0] == '.')
+ utf8_in(&c2, &word_end[2]);
+ else
+ utf8_in(&c2, &word_end[0]);
+
+ if((word_end[0] != 0) && (word_end[1] != 0) && ((c2 == 0) || (wtab[0].flags & FLAG_COMMA_AFTER) || IsAlpha(c2)))
+ {
+ // ordinal number is indicated by dot after the number
+ // but not if the next word starts with an upper-case letter
+ // (c2 == 0) is for cases such as, "2.,"
+ ordinal = 2;
+ if(word_end[0] == '.')
+ word_end[0] = ' ';
+
+ if((roman==0) && (tr->translator_name == L('h','u')))
+ {
+ // lang=hu don't treat dot as ordinal indicator if the next word is a month name ($alt). It may have a suffix.
+ nextflags = 0;
+ if(IsAlpha(c2))
+ {
+ nextflags = TranslateWord(tr, &word_end[2], 0, NULL, NULL);
+ }
+
+ if((tr->prev_dict_flags[0] & FLAG_ALT_TRANS) && ((c2 == 0) || (wtab[0].flags & FLAG_COMMA_AFTER) || iswdigit(c2)))
+ ordinal = 0; // TEST 09.02.10
+
+ if(nextflags & FLAG_ALT_TRANS)
+ ordinal = 0;
-int TranslateRoman(Translator *tr, char *word, char *ph_out)
-{//=====================================================
+ if(nextflags & FLAG_ALT3_TRANS)
+ {
+ if(word[-2] == '-')
+ ordinal = 0; // eg. december 2-5. között
+
+ if(tr->prev_dict_flags[0] & (FLAG_ALT_TRANS | FLAG_ALT3_TRANS))
+ ordinal = 0x22;
+ }
+ }
+ }
+ }
+ }
+ return(ordinal);
+} // end of CheckDotOrdinal
+
+
+static int hu_number_e(const char *word, int thousandplex, int value)
+{//==================================================================
+// lang-hu: variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt, att. ett
+
+ if((word[0] == 'a') || (word[0] == 'e'))
+ {
+ if((word[1] == ' ') || (word[1] == 'z') || ((word[1] == 't') && (word[2] == 't')))
+ return(0);
+
+ if(((thousandplex==1) || ((value % 1000) == 0)) && (word[1] == 'l'))
+ return(0); // 1000-el
+
+ return(1);
+ }
+ return(0);
+} // end of hu_numnber_e
+
+
+
+int TranslateRoman(Translator *tr, char *word, char *ph_out, WORD_TAB *wtab)
+{//=========================================================================
int c;
char *p;
const char *p2;
@@ -761,17 +1148,30 @@ int TranslateRoman(Translator *tr, char *word, char *ph_out)
int value;
int subtract;
int repeat = 0;
- unsigned int flags;
+ int n_digits = 0;
+ char *word_start;
+ int num_control = 0;
+ unsigned int flags[2];
char ph_roman[30];
char number_chars[N_WORD_BYTES];
static const char *roman_numbers = "ixcmvld";
static int roman_values[] = {1,10,100,1000,5,50,500};
-
+
acc = 0;
prev = 0;
subtract = 0x7fff;
+ ph_out[0] = 0;
+ flags[0] = 0;
+ flags[1] = 0;
+
+ if(((tr->langopts.numbers & NUM_ROMAN_CAPITALS) && !(wtab[0].flags & FLAG_ALL_UPPER)) || IsDigit09(word[-2]))
+ return(0); // not '2xx'
+ if(word[1] == ' ')
+ return(0); // only one letter, don't speak as a Roman Number
+
+ word_start = word;
while((c = *word++) != ' ')
{
if((p2 = strchr(roman_numbers,c)) == NULL)
@@ -799,253 +1199,512 @@ int TranslateRoman(Translator *tr, char *word, char *ph_out)
subtract = prev;
value -= subtract;
}
- else
- if(value >= subtract)
+ else if(value >= subtract)
return(0);
else
acc += prev;
prev = value;
+ n_digits++;
}
+
+ if(IsDigit09(word[0]))
+ return(0); // eg. 'xx2'
+
acc += prev;
- if(acc < 2)
+ if(acc < tr->langopts.min_roman)
return(0);
if(acc > tr->langopts.max_roman)
return(0);
+
Lookup(tr, "_roman",ph_roman); // precede by "roman" if _rom is defined in *_list
p = &ph_out[0];
if((tr->langopts.numbers & NUM_ROMAN_AFTER) == 0)
{
strcpy(ph_out,ph_roman);
- p = &ph_out[strlen(ph_out)];
+ p = &ph_out[strlen(ph_roman)];
}
- sprintf(number_chars," %d ",acc);
- TranslateNumber(tr, &number_chars[1], p, &flags, 0);
+ sprintf(number_chars," %d %s ",acc, tr->langopts.roman_suffix);
+
+ if(word[0] == '.')
+ {
+ // dot has not been removed. This implies that there was no space after it
+ return(0);
+ }
+
+ if(CheckDotOrdinal(tr, word_start, word, wtab, 1))
+ wtab[0].flags |= FLAG_ORDINAL;
+
+ if(tr->langopts.numbers & NUM_ROMAN_ORDINAL)
+ {
+ if(tr->translator_name == L('h','u'))
+ {
+ if(!(wtab[0].flags & FLAG_ORDINAL))
+ {
+ if((wtab[0].flags & FLAG_HYPHEN_AFTER) && hu_number_e(word, 0, acc))
+ {
+ // should use the 'e' form of the number
+ num_control |= 1;
+ }
+ else
+ return(0);
+ }
+ }
+ else
+ {
+ wtab[0].flags |= FLAG_ORDINAL;
+ }
+ }
+
+ tr->prev_dict_flags[0] = 0;
+ tr->prev_dict_flags[1] = 0;
+ TranslateNumber(tr, &number_chars[2], p, flags, wtab, num_control);
if(tr->langopts.numbers & NUM_ROMAN_AFTER)
strcat(ph_out,ph_roman);
+
return(1);
} // end of TranslateRoman
static const char *M_Variant(int value)
{//====================================
- // returns M, or perhaps MA for some cases
-
- if((translator->langopts.numbers2 & 0x100) && (value >= 2) && (value <= 4))
- return("0MA"); // Czech, Slovak
- else
- if(((value % 100) < 10) || ((value % 100) > 20)) // but not teens, 10 to 19
+ // returns M, or perhaps MA or MB for some cases
+
+ int teens = 0;
+
+ if(((value % 100) > 10) && ((value % 100) < 20))
+ teens = 1;
+
+ switch((translator->langopts.numbers2 >> 6) & 0x7)
{
- if ((translator->langopts.numbers2 & 0x40) &&
- ((value % 10)>=2) &&
- ((value % 10)<=4))
- {
- // for Polish language - two forms of plural!
+ case 1: // lang=ru use singular for xx1 except for x11
+ if((teens == 0) && ((value % 10) == 1))
+ return("1M");
+ break;
+
+ case 2: // lang=cs,sk
+ if((value >= 2) && (value <= 4))
return("0MA");
- }
+ break;
+
+ case 3: // lang=pl
+ if((teens == 0) && (((value % 10) >= 2) && ((value % 10) <= 4)))
+ return("0MA");
+ break;
+
+ case 4: // lang=lt
+ if((teens == 1) || ((value % 10) == 0))
+ return("0MB");
+ if((value % 10) == 1)
+ return("0MA");
+ break;
- if((translator->langopts.numbers2 & 0x80) &&
- ((value % 10)==1))
+ case 5: // lang=bs,hr,sr
+ if(teens == 0)
{
- return("1MA");
+ if((value % 10) == 1)
+ return("1M");
+ if(((value % 10) >= 2) && ((value % 10) <= 4))
+ return("0MA");
}
-
+ break;
}
return("0M");
}
-static int LookupThousands(Translator *tr, int value, int thousandplex, char *ph_out)
-{//==================================================================================
+static int LookupThousands(Translator *tr, int value, int thousandplex, int thousands_exact, char *ph_out)
+{//=======================================================================================================
+// thousands_exact: bit 0 no hundreds,tens,or units, bit 1 ordinal numberr
int found;
+ int found_value=0;
char string[12];
char ph_of[12];
char ph_thousands[40];
+ char ph_buf[40];
ph_of[0] = 0;
- // first look fora match with the exact value of thousands
- sprintf(string,"_%dM%d",value,thousandplex);
+ // first look for a match with the exact value of thousands
+ if(value > 0)
+ {
+ if(thousands_exact & 1)
+ {
+ if(thousands_exact & 2)
+ {
+ // ordinal number
+ sprintf(string,"_%dM%do",value,thousandplex);
+ found_value = Lookup(tr, string, ph_thousands);
+ }
+ if((!found_value) & (number_control & 1))
+ {
+ // look for the 'e' variant
+ sprintf(string,"_%dM%de",value,thousandplex);
+ found_value = Lookup(tr, string, ph_thousands);
+ }
+ if(!found_value)
+ {
+ // is there a different pronunciation if there are no hundreds,tens,or units ? (LANG=ta)
+ sprintf(string,"_%dM%dx",value,thousandplex);
+ found_value = Lookup(tr, string, ph_thousands);
+ }
+ }
+ if(found_value == 0)
+ {
+ sprintf(string,"_%dM%d",value,thousandplex);
+ found_value = Lookup(tr, string, ph_thousands);
+ }
+ }
- if((found = Lookup(tr, string, ph_thousands)) == 0)
+ if(found_value == 0)
{
- if((value % 100) >= 20)
+ if((value % 100) >= 20)
{
Lookup(tr, "_0of", ph_of);
}
- sprintf(string,"_%s%d",M_Variant(value),thousandplex);
-
- if(Lookup(tr, string, ph_thousands) == 0)
+ found = 0;
+ if(thousands_exact & 1)
+ {
+ if(thousands_exact & 2)
+ {
+ // ordinal number
+ sprintf(string,"_%s%do",M_Variant(value), thousandplex);
+ found = Lookup(tr, string, ph_thousands);
+ }
+ if(!found && (number_control & 1))
+ {
+ // look for the 'e' variant
+ sprintf(string,"_%s%de",M_Variant(value), thousandplex);
+ found = Lookup(tr, string, ph_thousands);
+ }
+ if(!found)
+ {
+ // is there a different pronunciation if there are no hundreds,tens,or units ?
+ sprintf(string,"_%s%dx",M_Variant(value), thousandplex);
+ found = Lookup(tr, string, ph_thousands);
+ }
+ }
+ if(found == 0)
{
- // repeat "thousand" if higher order names are not available
- sprintf(string,"_%dM1",value);
- if((found = Lookup(tr, string, ph_thousands)) == 0)
- Lookup(tr, "_0M1", ph_thousands);
+ sprintf(string,"_%s%d",M_Variant(value), thousandplex);
+
+ if(Lookup(tr, string, ph_thousands) == 0)
+ {
+ if(thousandplex > 3)
+ {
+ sprintf(string,"_0M%d", thousandplex-1);
+ if(Lookup(tr, string, ph_buf) == 0)
+ {
+ // say "millions" if this name is not available and neither is the next lower
+ Lookup(tr, "_0M2", ph_thousands);
+ speak_missing_thousands = 3;
+ }
+ }
+ if(ph_thousands[0] == 0)
+ {
+ // repeat "thousand" if higher order names are not available
+ sprintf(string,"_%dM1",value);
+ if((found_value = Lookup(tr, string, ph_thousands)) == 0)
+ Lookup(tr, "_0M1", ph_thousands);
+ speak_missing_thousands = 2;
+ }
+ }
}
}
sprintf(ph_out,"%s%s",ph_of,ph_thousands);
- return(found);
-}
+
+ if((value == 1) && (thousandplex == 1) && (tr->langopts.numbers & NUM_OMIT_1_THOUSAND))
+ return(1);
+
+ return(found_value);
+} // end f LookupThousands
-static int LookupNum2(Translator *tr, int value, int control, char *ph_out)
-{//========================================================================
+static int LookupNum2(Translator *tr, int value, int thousandplex, const int control, char *ph_out)
+{//=============================================================================
// Lookup a 2 digit number
-// control bit 0: tens and units (use special form of '1')
-// control bit 1: ordinal number
-// control bit 2: use feminine form of '2'
-// control bit 3: speak zero tens
+// control bit 0: ordinal number
+// control bit 1: final tens and units (not number of thousands) (use special form of '1', LANG=de "eins")
+// control bit 2: tens and units only, no higher digits
+// control bit 3: use feminine form of '2' (for thousands
+// control bit 4: speak zero tens
+// control bit 5: variant of ordinal number (lang=hu)
+// bit 8 followed by decimal fraction
+// bit 9: use #f form for both tens and units (lang=ml)
int found;
int ix;
int units;
+ int tens;
+ int is_ordinal;
int used_and=0;
int found_ordinal = 0;
int next_phtype;
+ int ord_type = 'o';
char string[12]; // for looking up entries in *_list
char ph_ordinal[20];
char ph_tens[50];
char ph_digits[50];
char ph_and[12];
- // is there a special pronunciation for this 2-digit number
+ units = value % 10;
+ tens = value / 10;
+
found = 0;
ph_ordinal[0] = 0;
+ ph_tens[0] = 0;
+ ph_digits[0] = 0;
+ ph_and[0] = 0;
- if(control & 4)
- {
- sprintf(string,"_%df",value);
- found = Lookup(tr, string, ph_digits);
- }
- if(control & 2)
- {
- strcpy(ph_ordinal, ph_ordinal2);
-
- sprintf(string,"_%do",value);
- if((found = Lookup(tr, string, ph_digits)) != 0)
- {
- found_ordinal = 1;
- }
- }
-
- if(found == 0)
+ if(control & 0x20)
{
- if((value == 1) && (control & 1))
- {
- if(Lookup(tr, "_1a", ph_out) != 0)
- return(0);
- }
- sprintf(string,"_%d",value);
- found = Lookup(tr, string, ph_digits);
+ ord_type = 'q';
}
- // no, speak as tens+units
+ is_ordinal = control & 1;
- if((control & 8) && (value < 10))
+ if((control & 2) && (n_digit_lookup == 2))
{
- // speak leading zero
- Lookup(tr, "_0", ph_tens);
+ // pronunciation of the final 2 digits has already been found
+ strcpy(ph_out, digit_lookup);
}
else
{
- if(found)
- {
- ph_tens[0] = 0;
- }
- else
+ if(digit_lookup[0] == 0)
{
- units = (value % 10);
-
- if((control & 2) && ((units == 0) || (tr->langopts.numbers & 0x10)))
+ // is there a special pronunciation for this 2-digit number
+ if(control & 8)
{
- sprintf(string,"_%dXo",value / 10);
- if(Lookup(tr, string, ph_tens) != 0)
+ // is there a feminine or thousands-variant form?
+ sprintf(string,"_%dfx",value);
+ if((found = Lookup(tr, string, ph_digits)) == 0)
{
- found_ordinal = 1;
+ sprintf(string,"_%df",value);
+ found = Lookup(tr, string, ph_digits);
}
}
- if(found_ordinal == 0)
+ else if(is_ordinal)
{
- sprintf(string,"_%dX",value / 10);
- Lookup(tr, string, ph_tens);
- }
+ strcpy(ph_ordinal, ph_ordinal2);
- if((ph_tens[0] == 0) && (tr->langopts.numbers & NUM_VIGESIMAL))
- {
- // tens not found, (for example) 73 is 60+13
- units = (value % 20);
- sprintf(string,"_%dX",(value / 10) & 0xfe);
- Lookup(tr, string, ph_tens);
+ if(control & 4)
+ {
+ sprintf(string,"_%d%cx",value,ord_type); // LANG=hu, special word for 1. 2. when there are no higher digits
+ if((found = Lookup(tr, string, ph_digits)) != 0)
+ {
+ if(ph_ordinal2x[0] != 0)
+ strcpy(ph_ordinal, ph_ordinal2x); // alternate pronunciation (lang=an)
+ }
+ }
+ if(found == 0)
+ {
+ sprintf(string,"_%d%c",value,ord_type);
+ found = Lookup(tr, string, ph_digits);
+ }
+ found_ordinal = found;
}
- ph_digits[0] = 0;
- if(units > 0)
- {
- found = 0;
- if(control & 4)
+ if(found == 0)
+ {
+ if(control & 2)
{
- // is there a variant form of this number?
- sprintf(string,"_%df",units);
+ // the final tens and units of a number
+ if(number_control & 1)
+ {
+ // look for 'e' variant
+ sprintf(string,"_%de",value);
+ found = Lookup(tr, string, ph_digits);
+ }
+ }
+ else
+ {
+ // followed by hundreds or thousands etc
+ if((tr->langopts.numbers2 & NUM2_ORDINAL_AND_THOUSANDS) && (thousandplex <= 1))
+ sprintf(string, "_%do", value); // LANG=TA
+ else
+ sprintf(string, "_%da", value);
found = Lookup(tr, string, ph_digits);
}
- if((control & 2) && ((tr->langopts.numbers & 0x10) == 0))
+
+ if(!found)
{
- // ordinal
- sprintf(string,"_%do",units);
- if((found = Lookup(tr, string, ph_digits)) != 0)
+ if((is_ordinal) && (tr->langopts.numbers2 & NUM2_NO_TEEN_ORDINALS))
+ {
+ // don't use numbers 10-99 to make ordinals, always use _1Xo etc (lang=pt)
+ }
+ else
+ {
+ sprintf(string,"_%d",value);
+ found = Lookup(tr, string, ph_digits);
+ }
+ }
+ }
+ }
+
+ // no, speak as tens+units
+
+ if((control & 0x10) && (value < 10))
+ {
+ // speak leading zero
+ Lookup(tr, "_0", ph_tens);
+ }
+ else
+ {
+ if(found)
+ {
+ ph_tens[0] = 0;
+ }
+ else
+ {
+
+ if(is_ordinal)
+ {
+ sprintf(string,"_%dX%c", tens, ord_type);
+ if(Lookup(tr, string, ph_tens) != 0)
{
found_ordinal = 1;
+
+ if((units != 0) && (tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL))
+ {
+ // Use the ordinal form of tens as well as units. Add the ordinal ending
+ strcat(ph_tens, ph_ordinal2);
+ }
}
}
- if(found == 0)
+ if(found_ordinal == 0)
+ {
+ if(control & 0x200)
+ sprintf(string, "_%dXf", tens);
+ else
+ sprintf(string,"_%dX", tens);
+ Lookup(tr, string, ph_tens);
+ }
+
+ if((ph_tens[0] == 0) && (tr->langopts.numbers & NUM_VIGESIMAL))
{
- sprintf(string,"_%d",units);
- Lookup(tr, string, ph_digits);
+ // tens not found, (for example) 73 is 60+13
+ units = (value % 20);
+ sprintf(string,"_%dX", tens & 0xfe);
+ Lookup(tr, string, ph_tens);
+ }
+
+ ph_digits[0] = 0;
+ if(units > 0)
+ {
+ found = 0;
+
+ if((control & 2) && (digit_lookup[0] != 0))
+ {
+ // we have an entry for this digit (possibly together with the next word)
+ strcpy(ph_digits, digit_lookup);
+ found_ordinal = 1;
+ ph_ordinal[0] = 0;
+ }
+ else
+ {
+ if(control & 8)
+ {
+ // is there a variant form of this number?
+ sprintf(string,"_%df",units);
+ found = Lookup(tr, string, ph_digits);
+ }
+ if((is_ordinal) && ((tr->langopts.numbers & NUM_SWAP_TENS) == 0))
+ {
+ // ordinal
+ sprintf(string,"_%d%c",units,ord_type);
+ if((found = Lookup(tr, string, ph_digits)) != 0)
+ {
+ found_ordinal = 1;
+ }
+ }
+ if(found == 0)
+ {
+ if((number_control & 1) && (control & 2))
+ {
+ // look for 'e' variant
+ sprintf(string,"_%de",units);
+ found = Lookup(tr, string, ph_digits);
+ }
+ else if(((control & 2) == 0) || ((tr->langopts.numbers & NUM_SWAP_TENS) != 0))
+ {
+ // followed by hundreds or thousands (or tens)
+ if((tr->langopts.numbers2 & NUM2_ORDINAL_AND_THOUSANDS) && (thousandplex <= 1))
+ sprintf(string, "_%do", units); // LANG=TA, only for 100s, 1000s
+ else
+ sprintf(string, "_%da", units);
+ found = Lookup(tr, string, ph_digits);
+ }
+ }
+ if(found == 0)
+ {
+ sprintf(string,"_%d",units);
+ Lookup(tr, string, ph_digits);
+ }
+ }
}
}
}
- }
- if((control & 2) && (found_ordinal == 0) && (ph_ordinal[0] == 0))
- {
- if((value >= 20) && (((value % 10) == 0) || (tr->langopts.numbers & 0x10)))
- Lookup(tr, "_ord20", ph_ordinal);
- if(ph_ordinal[0] == 0)
- Lookup(tr, "_ord", ph_ordinal);
- }
+ if((is_ordinal) && (found_ordinal == 0) && (ph_ordinal[0] == 0))
+ {
+ if((value >= 20) && (((value % 10) == 0) || (tr->langopts.numbers & NUM_SWAP_TENS)))
+ Lookup(tr, "_ord20", ph_ordinal);
+ if(ph_ordinal[0] == 0)
+ Lookup(tr, "_ord", ph_ordinal);
+ }
- if((tr->langopts.numbers & 0x30) && (ph_tens[0] != 0) && (ph_digits[0] != 0))
- {
- Lookup(tr, "_0and", ph_and);
- if(tr->langopts.numbers & 0x10)
- sprintf(ph_out,"%s%s%s%s",ph_digits, ph_and, ph_tens, ph_ordinal);
+ if((tr->langopts.numbers & (NUM_SWAP_TENS | NUM_AND_UNITS)) && (ph_tens[0] != 0) && (ph_digits[0] != 0))
+ {
+ Lookup(tr, "_0and", ph_and);
+
+ if((is_ordinal) && (tr->langopts.numbers2 & NUM2_ORDINAL_NO_AND))
+ ph_and[0] = 0;
+
+ if(tr->langopts.numbers & NUM_SWAP_TENS)
+ sprintf(ph_out,"%s%s%s%s",ph_digits, ph_and, ph_tens, ph_ordinal);
+ else
+ sprintf(ph_out,"%s%s%s%s",ph_tens, ph_and, ph_digits, ph_ordinal);
+ used_and = 1;
+ }
else
- sprintf(ph_out,"%s%s%s%s",ph_tens, ph_and, ph_digits, ph_ordinal);
- used_and = 1;
+ {
+ if(tr->langopts.numbers & NUM_SINGLE_VOWEL)
+ {
+ // remove vowel from the end of tens if units starts with a vowel (LANG=Italian)
+ if(((ix = strlen(ph_tens)-1) >= 0) && (ph_digits[0] != 0))
+ {
+ if((next_phtype = phoneme_tab[(unsigned int)(ph_digits[0])]->type) == phSTRESS)
+ next_phtype = phoneme_tab[(unsigned int)(ph_digits[1])]->type;
+
+ if((phoneme_tab[(unsigned int)(ph_tens[ix])]->type == phVOWEL) && (next_phtype == phVOWEL))
+ ph_tens[ix] = 0;
+ }
+ }
+ sprintf(ph_out,"%s%s%s",ph_tens, ph_digits, ph_ordinal);
+ }
}
- else
+
+ if(tr->langopts.numbers & NUM_SINGLE_STRESS_L)
{
- if(tr->langopts.numbers & 0x200)
+ // only one primary stress, on the first part (tens)
+ found = 0;
+ for(ix=0; ix < (signed)strlen(ph_out); ix++)
{
- // remove vowel from the end of tens if units starts with a vowel (LANG=Italian)
- if(((ix = strlen(ph_tens)-1) >= 0) && (ph_digits[0] != 0))
+ if(ph_out[ix] == phonSTRESS_P)
{
- if((next_phtype = phoneme_tab[(unsigned int)(ph_digits[0])]->type) == phSTRESS)
- next_phtype = phoneme_tab[(unsigned int)(ph_digits[1])]->type;
-
- if((phoneme_tab[(unsigned int)(ph_tens[ix])]->type == phVOWEL) && (next_phtype == phVOWEL))
- ph_tens[ix] = 0;
+ if(found)
+ ph_out[ix] = phonSTRESS_3;
+ else
+ found = 1;
}
}
- sprintf(ph_out,"%s%s%s",ph_tens, ph_digits, ph_ordinal);
}
-
- if(tr->langopts.numbers & 0x100)
+ else if(tr->langopts.numbers & NUM_SINGLE_STRESS)
{
// only one primary stress
found = 0;
@@ -1069,9 +1728,18 @@ static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null
// Translate a 3 digit number
// control bit 0, previous thousands
// bit 1, ordinal number
+// bit 5 variant form of ordinal number
+// bit 8 followed by decimal fraction
int found;
int hundreds;
+ int tensunits;
int x;
+ int ix;
+ int exact;
+ int ordinal;
+ int tplex;
+ int say_zero_hundred=0;
+ int say_one_hundred;
char string[12]; // for looking up entries in **_list
char buf1[100];
char buf2[100];
@@ -1081,59 +1749,87 @@ static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null
char ph_thousands[50];
char ph_hundred_and[12];
char ph_thousand_and[12];
-
+
+ ordinal = control & 0x22;
hundreds = value / 100;
+ tensunits = value % 100;
buf1[0] = 0;
- if(hundreds > 0)
+ ph_thousands[0] = 0;
+ ph_thousand_and[0] = 0;
+
+ if((tr->langopts.numbers & NUM_ZERO_HUNDRED) && ((control & 1) || (hundreds >= 10)))
{
- ph_thousands[0] = 0;
- ph_thousand_and[0] = 0;
+ say_zero_hundred = 1; // lang=vi
+ }
+ if((hundreds > 0) || say_zero_hundred)
+ {
found = 0;
- if((control & 2) && ((value % 100) == 0))
+ if(ordinal && (tensunits == 0))
{
// ordinal number, with no tens or units
found = Lookup(tr, "_0Co", ph_100);
}
if(found == 0)
{
- Lookup(tr, "_0C", ph_100);
+ if(tensunits==0)
+ {
+ // special form for exact hundreds?
+ found = Lookup(tr, "_0C0", ph_100);
+ }
+ if(!found)
+ {
+ Lookup(tr, "_0C", ph_100);
+ }
}
- if(((tr->langopts.numbers & 0x0800) != 0) && (hundreds == 19))
+ if(((tr->langopts.numbers & NUM_1900) != 0) && (hundreds == 19))
{
// speak numbers such as 1984 as years: nineteen-eighty-four
// ph_100[0] = 0; // don't say "hundred", we also need to surpess "and"
}
- else
- if(hundreds >= 10)
+ else if(hundreds >= 10)
{
ph_digits[0] = 0;
- if(LookupThousands(tr, hundreds / 10, thousandplex+1, ph_10T) == 0)
+ exact = 0;
+ if ((value % 1000) == 0)
+ exact = 1;
+
+ tplex = thousandplex+1;
+ if(tr->langopts.numbers2 & NUM2_MYRIADS)
+ {
+ tplex = 0;
+ }
+
+ if(LookupThousands(tr, hundreds / 10, tplex, exact | ordinal, ph_10T) == 0)
{
x = 0;
- if(tr->langopts.numbers2 & (1 << (thousandplex+1)))
- x = 4;
- LookupNum2(tr, hundreds/10, x, ph_digits);
+ if(tr->langopts.numbers2 & (1 << tplex))
+ x = 8; // use variant (feminine) for before thousands and millions
+ if(tr->translator_name == L('m','l'))
+ x = 0x208;
+ LookupNum2(tr, hundreds/10, thousandplex, x, ph_digits);
}
if(tr->langopts.numbers2 & 0x200)
- sprintf(ph_thousands,"%s%s%c",ph_10T,ph_digits,phonPAUSE_NOLINK); // say "thousands" before its number, not after
+ sprintf(ph_thousands,"%s%c%s%c",ph_10T,phonEND_WORD,ph_digits,phonEND_WORD); // say "thousands" before its number, not after
else
- sprintf(ph_thousands,"%s%s%c",ph_digits,ph_10T,phonPAUSE_NOLINK);
+ sprintf(ph_thousands,"%s%c%s%c",ph_digits,phonEND_WORD,ph_10T,phonEND_WORD);
hundreds %= 10;
- if(hundreds == 0)
+ if((hundreds == 0) && (say_zero_hundred == 0))
ph_100[0] = 0;
suppress_null = 1;
+ control |= 1;
}
ph_digits[0] = 0;
- if(hundreds > 0)
+
+ if((hundreds > 0) || say_zero_hundred)
{
- if((tr->langopts.numbers & 0x100000) && ((control & 1) || (ph_thousands[0] != 0)))
+ if((tr->langopts.numbers & NUM_AND_HUNDRED) && ((control & 1) || (ph_thousands[0] != 0)))
{
Lookup(tr, "_0and", ph_thousand_and);
}
@@ -1141,26 +1837,64 @@ static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null
suppress_null = 1;
found = 0;
- if((value % 1000) == 100)
- {
- // is there a special pronunciation for exactly 100 ?
- found = Lookup(tr, "_1C0", ph_digits);
- }
- if(!found)
+ if((ordinal)
+ && ((tensunits == 0) || (tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL)))
{
- sprintf(string,"_%dC",hundreds);
- found = Lookup(tr, string, ph_digits); // is there a specific pronunciation for n-hundred ?
+ // ordinal number
+ sprintf(string, "_%dCo", hundreds);
+ found = Lookup(tr, string, ph_digits);
+
+ if((tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL) && (tensunits > 0))
+ {
+ // Use ordinal form of hundreds, as well as for tens and units
+ // Add ordinal suffix to the hundreds
+ strcat(ph_digits, ph_ordinal2);
+ }
}
- if(found)
+ if((hundreds == 0) && say_zero_hundred)
{
- ph_100[0] = 0;
+ Lookup(tr, "_0", ph_digits);
}
else
{
- if((hundreds > 1) || ((tr->langopts.numbers & 0x400) == 0))
+ if((hundreds==1) && (tr->langopts.numbers2 & NUM2_OMIT_1_HUNDRED_ONLY) && ((control & 1)==0))
+ {
+ // only look for special 100 if there are previous thousands
+ }
+ else
+ {
+ if((!found) && (tensunits == 0))
+ {
+ // is there a special pronunciation for exactly n00 ?
+ sprintf(string,"_%dC0",hundreds);
+ found = Lookup(tr, string, ph_digits);
+ }
+
+ if(!found)
+ {
+ sprintf(string,"_%dC",hundreds);
+ found = Lookup(tr, string, ph_digits); // is there a specific pronunciation for n-hundred ?
+ }
+ }
+
+ if(found)
+ {
+ ph_100[0] = 0;
+ }
+ else
{
- LookupNum2(tr, hundreds, 0, ph_digits);
+ say_one_hundred = 1;
+ if(hundreds == 1)
+ {
+ if((tr->langopts.numbers & NUM_OMIT_1_HUNDRED) != 0)
+ say_one_hundred = 0;
+ }
+
+ if(say_one_hundred != 0)
+ {
+ LookupNum2(tr, hundreds, thousandplex, 0, ph_digits);
+ }
}
}
}
@@ -1169,104 +1903,195 @@ static int LookupNum3(Translator *tr, int value, char *ph_out, int suppress_null
}
ph_hundred_and[0] = 0;
- if((tr->langopts.numbers & 0x40) && ((value % 100) != 0))
+ if(tensunits > 0)
{
- if((value > 100) || ((control & 1) && (thousandplex==0)))
+ if((control & 2) && (tr->langopts.numbers2 & NUM2_MULTIPLE_ORDINAL))
{
- Lookup(tr, "_0and", ph_hundred_and);
+ // Don't use "and" if we apply ordinal to both hundreds and units
+ }
+ else
+ {
+ if((value > 100) || ((control & 1) && (thousandplex==0)))
+ {
+ if((tr->langopts.numbers & NUM_HUNDRED_AND) || ((tr->langopts.numbers & NUM_HUNDRED_AND_DIGIT) && (tensunits < 10)))
+ {
+ Lookup(tr, "_0and", ph_hundred_and);
+ }
+ }
+ if((tr->langopts.numbers & NUM_THOUSAND_AND) && (hundreds == 0) && ((control & 1) || (ph_thousands[0] != 0)))
+ {
+ Lookup(tr, "_0and", ph_hundred_and);
+ }
}
}
buf2[0] = 0;
- value = value % 100;
- if((value != 0) || (suppress_null == 0))
+ if((tensunits != 0) || (suppress_null == 0))
{
x = 0;
if(thousandplex==0)
{
- x = 1; // allow "eins" for 1 rather than "ein"
- if(control & 2)
+ x = 2; // allow "eins" for 1 rather than "ein"
+ if(ordinal)
x = 3; // ordinal number
+ if((value < 100) && !(control & 1))
+ x |= 4; // tens and units only, no higher digits
+ if(ordinal & 0x20)
+ x |= 0x20; // variant form of ordinal number
}
else
{
if(tr->langopts.numbers2 & (1 << thousandplex))
- x = 4; // use variant (feminine) for before thousands and millions
+ x = 8; // use variant (feminine) for before thousands and millions
+ }
+
+ if((tr->translator_name == L('m','l')) && (thousandplex == 1))
+ {
+ x |= 0x208; // use #f form for both tens and units
}
- if(LookupNum2(tr, value, x, buf2) != 0)
+ if(LookupNum2(tr, tensunits, thousandplex, x | (control & 0x100), buf2) != 0)
{
- if(tr->langopts.numbers & 0x80)
+ if(tr->langopts.numbers & NUM_SINGLE_AND)
ph_hundred_and[0] = 0; // don't put 'and' after 'hundred' if there's 'and' between tens and units
}
}
+ else
+ {
+ if(ph_ordinal2[0] != 0)
+ {
+ ix = strlen(buf1);
+ if((ix > 0) && (buf1[ix-1] == phonPAUSE_SHORT))
+ buf1[ix-1] = 0; // remove pause before addding ordinal suffix
+ strcpy(buf2, ph_ordinal2);
+ }
+ }
- sprintf(ph_out,"%s%s%s",buf1,ph_hundred_and,buf2);
+ sprintf(ph_out,"%s%s%c%s",buf1,ph_hundred_and,phonEND_WORD,buf2);
return(0);
} // end of LookupNum3
-static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned int *flags, int wflags)
-{//====================================================================================================
+static bool CheckThousandsGroup(char *word, int group_len)
+{//================================================
+// Is this a group of 3 digits which looks like a thousands group?
+ int ix;
+
+ if(IsDigit09(word[group_len]) || IsDigit09(-1))
+ return(false);
+
+ for(ix=0; ix < group_len; ix++)
+ {
+ if(!IsDigit09(word[ix]))
+ return(false);
+ }
+ return(true);
+}
+
+
+static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control)
+{//=====================================================================================================================
// Number translation with various options
// the "word" may be up to 4 digits
// "words" of 3 digits may be preceded by another number "word" for thousands or millions
int n_digits;
int value;
- unsigned int ix;
+ int ix;
+ int digix;
unsigned char c;
int suppress_null = 0;
int decimal_point = 0;
int thousandplex = 0;
+ int thousands_exact = 1;
int thousands_inc = 0;
int prev_thousands = 0;
int ordinal = 0;
int this_value;
- static int prev_value;
int decimal_count;
int max_decimal_count;
int decimal_mode;
- int hyphen;
+ int suffix_ix;
+ int skipwords = 0;
+ int group_len;
+ int len;
char *p;
- char string[20]; // for looking up entries in **_list
+ char string[32]; // for looking up entries in **_list
char buf1[100];
char ph_append[50];
char ph_buf[200];
char ph_buf2[50];
- char suffix[20];
+ char ph_zeros[50];
+ char suffix[30]; // string[] must be long enough for sizeof(suffix)+2
+ char buf_digit_lookup[50];
static const char str_pause[2] = {phonPAUSE_NOLINK,0};
*flags = 0;
+ n_digit_lookup = 0;
+ buf_digit_lookup[0] = 0;
+ digit_lookup = buf_digit_lookup;
+ number_control = control;
- for(ix=0; isdigit(word[ix]); ix++) ;
+ for(ix=0; IsDigit09(word[ix]); ix++) ;
n_digits = ix;
value = this_value = atoi(word);
+ group_len = 3;
+ if(tr->langopts.numbers2 & NUM2_MYRIADS)
+ group_len = 4;
+
+ // is there a previous thousands part (as a previous "word") ?
+ if((n_digits == group_len) && (word[-2] == tr->langopts.thousands_sep) && IsDigit09(word[-3]))
+ {
+ prev_thousands = 1;
+ }
+ else if((tr->langopts.thousands_sep == ' ') || (tr->langopts.numbers & NUM_ALLOW_SPACE))
+ {
+ // thousands groups can be separated by spaces
+ if((n_digits == 3) && !(wtab->flags & FLAG_MULTIPLE_SPACES) && IsDigit09(word[-2]))
+ {
+ prev_thousands = 1;
+ }
+ }
+ if(prev_thousands == 0)
+ {
+ speak_missing_thousands = 0;
+ }
+
ph_ordinal2[0] = 0;
- if((tr->langopts.numbers & 0x10000) && (word[ix] == '.') && !isdigit(word[ix+2]))
+ ph_zeros[0] = 0;
+
+ if(prev_thousands || (word[0] != '0'))
{
- // ordinal number is indicated by dot after the number
- ordinal = 2;
- word[ix] = ' ';
+ // don't check for ordinal if the number has a leading zero
+ if((ordinal = CheckDotOrdinal(tr, word, &word[ix], wtab, 0)) != 0)
+ {
+// dot_ordinal = 1;
+ }
}
- else
+
+ if((word[ix] == '.') && !IsDigit09(word[ix+1]) && !IsDigit09(word[ix+2]) && !(wtab[1].flags & FLAG_NOSPACE))
{
+ // remove dot unless followed by another number
+ word[ix] = 0;
+ }
+
+ if((ordinal == 0) || (tr->translator_name == L('h','u')))
+ {
+// NOTE lang=hu, allow both dot and ordinal suffix, eg. "december 21.-én"
// look for an ordinal number suffix after the number
ix++;
- hyphen = 0;
p = suffix;
- if(word[ix] == '-')
+ if(wtab[0].flags & FLAG_HYPHEN_AFTER)
{
*p++ = '-';
- hyphen = 1;
- ix += 2;
+ ix++;
}
- while((word[ix] != 0) && (word[ix] != ' ') && (ix < (sizeof(suffix)-1)))
+ while((word[ix] != 0) && (word[ix] != ' ') && (ix < (int)(sizeof(suffix)-1)))
{
*p++ = word[ix++];
}
@@ -1274,64 +2099,85 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
if(suffix[0] != 0)
{
- sprintf(string,"_0%s",suffix);
- if(Lookup(tr, string, ph_ordinal2))
+ if((tr->langopts.ordinal_indicator != NULL) && (strcmp(suffix, tr->langopts.ordinal_indicator) == 0))
{
- // this is an ordinal suffix
ordinal = 2;
- flags[0] |= FLAG_SKIPWORDS;
- dictionary_skipwords = 1 + hyphen;
+ }
+ else if(!IsDigit09(suffix[0])) // not _#9 (tab)
+ {
+ sprintf(string,"_#%s",suffix);
+ if(Lookup(tr, string, ph_ordinal2))
+ {
+ // this is an ordinal suffix
+ ordinal = 2;
+ flags[0] |= FLAG_SKIPWORDS;
+ skipwords = 1;
+ sprintf(string,"_x#%s",suffix);
+ Lookup(tr, string, ph_ordinal2x); // is there an alternate pronunciation?
+ }
}
}
}
+ if(wtab[0].flags & FLAG_ORDINAL)
+ ordinal = 2;
+
ph_append[0] = 0;
ph_buf2[0] = 0;
- // is there a previous thousands part (as a previous "word") ?
- if((n_digits == 3) && (word[-2] == tr->langopts.thousands_sep) && isdigit(word[-3]))
- {
- prev_thousands = 1;
- }
- else
- if((tr->langopts.thousands_sep == ' ') || (tr->langopts.numbers & 0x1000))
- {
- // thousands groups can be separated by spaces
- if((n_digits == 3) && isdigit(word[-2]))
- {
- prev_thousands = 1;
- }
- }
if((word[0] == '0') && (prev_thousands == 0) && (word[1] != ' ') && (word[1] != tr->langopts.decimal_sep))
{
- if((n_digits == 2) && (word[3] == ':') && isdigit(word[5]) && isspace(word[7]))
+ if((n_digits == 2) && (word[3] == ':') && IsDigit09(word[5]) && isspace(word[7]))
{
// looks like a time 02:30, omit the leading zero
}
else
{
- return(0); // number string with leading zero, speak as individual digits
+ if(n_digits > 3)
+ {
+ flags[0] &= ~FLAG_SKIPWORDS;
+ return(0); // long number string with leading zero, speak as individual digits
+ }
+
+ // speak leading zeros
+ for(ix=0; (word[ix] == '0') && (ix < (n_digits-1)); ix++)
+ {
+ Lookup(tr, "_0", &ph_zeros[strlen(ph_zeros)]);
+ }
}
}
- if((tr->langopts.numbers & 0x1000) && (word[n_digits] == ' '))
+ if((tr->langopts.numbers & NUM_ALLOW_SPACE) && (word[n_digits] == ' '))
thousands_inc = 1;
- else
- if(word[n_digits] == tr->langopts.thousands_sep)
+ else if(word[n_digits] == tr->langopts.thousands_sep)
thousands_inc = 2;
+ suffix_ix = n_digits+2;
if(thousands_inc > 0)
{
// if the following "words" are three-digit groups, count them and add
// a "thousand"/"million" suffix to this one
+ digix = n_digits + thousands_inc;
- ix = n_digits + thousands_inc;
- while(isdigit(word[ix]) && isdigit(word[ix+1]) && isdigit(word[ix+2]))
+ while(((wtab[thousandplex+1].flags & FLAG_MULTIPLE_SPACES) == 0) && CheckThousandsGroup(&word[digix], group_len))
{
+ for(ix=0; ix<group_len; ix++)
+ {
+ if(word[digix+ix] != '0')
+ {
+ thousands_exact = 0;
+ break;
+ }
+ }
+
thousandplex++;
- if(word[ix+3] == tr->langopts.thousands_sep)
- ix += (3 + thousands_inc);
+ digix += group_len;
+ if((word[digix] == tr->langopts.thousands_sep) || ((tr->langopts.numbers & NUM_ALLOW_SPACE) && (word[digix] == ' ')))
+ {
+ suffix_ix = digix+2;
+ digix += thousands_inc;
+ }
else
break;
}
@@ -1342,20 +2188,29 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
suppress_null = 1;
}
- if((word[n_digits] == tr->langopts.decimal_sep) && isdigit(word[n_digits+1]))
+ if(tr->translator_name == L('h','u'))
+ {
+ // variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt
+ if((wtab[thousandplex].flags & FLAG_HYPHEN_AFTER) && (thousands_exact==1) && hu_number_e(&word[suffix_ix], thousandplex, value))
+ {
+ number_control |= 1; // use _1e variant of number
+ }
+ }
+
+ if((word[n_digits] == tr->langopts.decimal_sep) && IsDigit09(word[n_digits+1]))
{
// this "word" ends with a decimal point
Lookup(tr, "_dpt", ph_append);
- decimal_point = 1;
+ decimal_point = 0x100;
}
- else
- if(suppress_null == 0)
+ else if(suppress_null == 0)
{
if(thousands_inc > 0)
{
- if((thousandplex > 0) && (value < 1000))
+ if(thousandplex > 0)
+// if((thousandplex > 0) && (value < 1000))
{
- if((suppress_null == 0) && (LookupThousands(tr,value,thousandplex,ph_append)))
+ if((suppress_null == 0) && (LookupThousands(tr,value,thousandplex, thousands_exact, ph_append)))
{
// found an exact match for N thousand
value = 0;
@@ -1365,27 +2220,85 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
}
}
else
- if((thousandplex > 1) && prev_thousands && (prev_value > 0))
- {
- sprintf(string,"_%s%d",M_Variant(value),thousandplex+1);
- if(Lookup(tr, string, buf1)==0)
+
+ if(speak_missing_thousands == 1)
{
// speak this thousandplex if there was no word for the previous thousandplex
- sprintf(string,"_0M%d",thousandplex);
- Lookup(tr, string, ph_append);
+ sprintf(string,"_0M%d",thousandplex+1);
+ if(Lookup(tr, string, buf1)==0)
+ {
+ sprintf(string,"_0M%d",thousandplex);
+ Lookup(tr, string, ph_append);
+ }
}
- }
if((ph_append[0] == 0) && (word[n_digits] == '.') && (thousandplex == 0))
{
Lookup(tr, "_.", ph_append);
}
- LookupNum3(tr, value, ph_buf, suppress_null, thousandplex, prev_thousands | ordinal);
+ if(thousandplex == 0)
+ {
+ char *p2;
+ // look for combinations of the number with the next word
+ p = word;
+ while(IsDigit09(p[1])) p++; // just use the last digit
+ if(IsDigit09(p[-1]))
+ {
+ p2 = p - 1;
+ if(LookupDictList(tr, &p2, buf_digit_lookup, flags, FLAG_SUFX, wtab)) // lookup 2 digits
+ {
+ n_digit_lookup = 2;
+ }
+ }
+
+// if((buf_digit_lookup[0] == 0) && (*p != '0') && (dot_ordinal==0))
+ if((buf_digit_lookup[0] == 0) && (*p != '0'))
+ {
+ // LANG=hu ?
+ // not found, lookup only the last digit (?? but not if dot-ordinal has been found)
+ if(LookupDictList(tr, &p, buf_digit_lookup, flags, FLAG_SUFX, wtab)) // don't match '0', or entries with $only
+ {
+ n_digit_lookup = 1;
+ }
+ }
+
+ if(prev_thousands == 0)
+ {
+ if((decimal_point == 0) && (ordinal == 0))
+ {
+ // Look for special pronunciation for this number in isolation (LANG=kl)
+ sprintf(string, "_%dn", value);
+ if(Lookup(tr, string, ph_out))
+ {
+ return(1);
+ }
+ }
+
+ if(tr->langopts.numbers2 & NUM2_PERCENT_BEFORE)
+ {
+ // LANG=si, say "percent" before the number
+ p2 = word;
+ while((*p2 != ' ') && (*p2 != 0))
+ {
+ p2++;
+ }
+ if(p2[1] == '%')
+ {
+ Lookup(tr, "%", ph_out);
+ ph_out += strlen(ph_out);
+ p2[1] = ' ';
+ }
+ }
+ }
+
+ }
+
+ LookupNum3(tr, value, ph_buf, suppress_null, thousandplex, prev_thousands | ordinal | decimal_point);
if((thousandplex > 0) && (tr->langopts.numbers2 & 0x200))
- sprintf(ph_out,"%s%s%s",ph_append,ph_buf2,ph_buf); // say "thousands" before its number
+ sprintf(ph_out,"%s%s%c%s%s",ph_zeros,ph_append,phonEND_WORD,ph_buf2,ph_buf); // say "thousands" before its number
else
- sprintf(ph_out,"%s%s%s",ph_buf2,ph_buf,ph_append);
+ sprintf(ph_out,"%s%s%s%c%s",ph_zeros,ph_buf2,ph_buf,phonEND_WORD,ph_append);
while(decimal_point)
@@ -1393,17 +2306,17 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
n_digits++;
decimal_count = 0;
- while(isdigit(word[n_digits+decimal_count]))
+ while(IsDigit09(word[n_digits+decimal_count]))
decimal_count++;
- if(decimal_count > 1)
+// if(decimal_count > 1)
{
max_decimal_count = 2;
switch(decimal_mode = (tr->langopts.numbers & 0xe000))
{
- case 0x8000:
+ case NUM_DFRACTION_4:
max_decimal_count = 5;
- case 0x4000:
+ case NUM_DFRACTION_2:
// French/Polish decimal fraction
while(word[n_digits] == '0')
{
@@ -1412,7 +2325,7 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
decimal_count--;
n_digits++;
}
- if((decimal_count <= max_decimal_count) && isdigit(word[n_digits]))
+ if((decimal_count <= max_decimal_count) && IsDigit09(word[n_digits]))
{
LookupNum3(tr, atoi(&word[n_digits]), buf1, 0,0,0);
strcat(ph_out,buf1);
@@ -1420,50 +2333,63 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
}
break;
- case 0x2000:
- case 0xa000:
- // Italian decimal fractions
- if(decimal_count <= 4)
+ case NUM_DFRACTION_1: // italian, say "hundredths" if leading zero
+ case NUM_DFRACTION_5: // hungarian, always say "tenths" etc.
+ case NUM_DFRACTION_6: // kazakh, always say "tenths" etc, before the decimal fraction
+ LookupNum3(tr, atoi(&word[n_digits]), ph_buf, 0,0,0);
+ if((word[n_digits]=='0') || (decimal_mode != NUM_DFRACTION_1))
{
- LookupNum3(tr, atoi(&word[n_digits]), ph_buf, 0,0,0);
- if((word[n_digits]=='0') || (decimal_mode == 0xa000))
- {
- // decimal part has leading zeros, so add a "hundredths" or "thousandths" suffix
- sprintf(string,"_0Z%d",decimal_count);
- if(Lookup(tr, string, buf1) == 0)
- break; // revert to speaking single digits
+ // decimal part has leading zeros, so add a "hundredths" or "thousandths" suffix
+ sprintf(string,"_0Z%d",decimal_count);
+ if(Lookup(tr, string, buf1) == 0)
+ break; // revert to speaking single digits
- strcat(ph_buf,buf1);
- }
- strcat(ph_out,ph_buf);
- n_digits += decimal_count;
+ if(decimal_mode == NUM_DFRACTION_6)
+ strcat(ph_out, buf1);
+ else
+ strcat(ph_buf, buf1);
}
+ strcat(ph_out,ph_buf);
+ n_digits += decimal_count;
break;
- case 0x6000:
+ case NUM_DFRACTION_3:
// Romanian decimal fractions
if((decimal_count <= 4) && (word[n_digits] != '0'))
{
- LookupNum3(tr, atoi(&word[n_digits]), buf1, 0,0,0);
- strcat(ph_out,buf1);
- n_digits += decimal_count;
+ LookupNum3(tr, atoi(&word[n_digits]), buf1, 0,0,0);
+ strcat(ph_out,buf1);
+ n_digits += decimal_count;
}
break;
+
+ case NUM_DFRACTION_7:
+ // alternative form of decimal fraction digits, except the final digit
+ while(decimal_count-- > 1)
+ {
+ sprintf(string,"_%cd", word[n_digits]);
+ if(Lookup(tr, string, buf1) == 0)
+ break;
+ n_digits++;
+ strcat(ph_out, buf1);
+ }
}
}
- while(isdigit(c = word[n_digits]) && (strlen(ph_out) < (N_WORD_PHONEMES - 10)))
+ while(IsDigit09(c = word[n_digits]) && (strlen(ph_out) < (N_WORD_PHONEMES - 10)))
{
+ // speak any remaining decimal fraction digits individually
value = word[n_digits++] - '0';
- LookupNum2(tr, value, 1, buf1);
- strcat(ph_out,buf1);
+ LookupNum2(tr, value, 0, 2, buf1);
+ len = strlen(ph_out);
+ sprintf(&ph_out[len],"%c%s", phonEND_WORD, buf1);
}
// something after the decimal part ?
if(Lookup(tr, "_dpt2", buf1))
strcat(ph_out,buf1);
- if((c == tr->langopts.decimal_sep) && isdigit(word[n_digits+1]))
+ if((c == tr->langopts.decimal_sep) && IsDigit09(word[n_digits+1]))
{
Lookup(tr, "_dpt", buf1);
strcat(ph_out,buf1);
@@ -1483,25 +2409,30 @@ static int TranslateNumber_1(Translator *tr, char *word, char *ph_out, unsigned
if((tr->langopts.numbers & NUM_NOPAUSE) && (next_char == ' '))
utf8_in(&next_char,p);
- if(!iswalpha(next_char))
+ if(!iswalpha2(next_char) && (thousands_exact==0))
+// if(!iswalpha2(next_char) && !((wtab[thousandplex].flags & FLAG_HYPHEN_AFTER) && (thousands_exact != 0)))
strcat(ph_out,str_pause); // don't add pause for 100s, 6th, etc.
}
*flags |= FLAG_FOUND;
- prev_value = this_value;
+ speak_missing_thousands--;
+
+ if(skipwords)
+ dictionary_skipwords = skipwords;
return(1);
} // end of TranslateNumber_1
-int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, int wflags)
-{//============================================================================================
- if(option_sayas == SAYAS_DIGITS1)
+int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control)
+{//=============================================================================================================
+ if((option_sayas == SAYAS_DIGITS1) || (wtab[0].flags & FLAG_INDIVIDUAL_DIGITS))
return(0); // speak digits individually
- if((tr->langopts.numbers & 0x3) == 1)
- return(TranslateNumber_1(tr, word1, ph_out, flags, wflags));
-
+ if(tr->langopts.numbers != 0)
+ {
+ return(TranslateNumber_1(tr, word1, ph_out, flags, wtab, control));
+ }
return(0);
} // end of TranslateNumber
diff --git a/navit/support/espeak/phoneme.h b/navit/support/espeak/phoneme.h
index 596f457ef..9a2e13913 100755..100644
--- a/navit/support/espeak/phoneme.h
+++ b/navit/support/espeak/phoneme.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2010 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -35,7 +35,8 @@
// phoneme properties
-// bits 16-19 give place of articulation (not currently used)
+// bits 16-19 give place of articulation
+#define phARTICULATION 0xf0000
#define phWAVE 0x01
#define phUNSTRESSED 0x02
#define phFORTIS 0x08
@@ -45,22 +46,20 @@
#define phTRILL 0x80
#define phVOWEL2 0x100 // liquid that is considered a vowel
#define phPALATAL 0x200
-#define phAPPENDPH 0x2000 // always insert another phoneme (link_out) after this one
+#define phSINGLE_INSTN 0x1000 // this phoneme has a single instruction program, with an implicit Return
#define phBRKAFTER 0x4000 // [*] add a post-pause
-#define phBEFOREPAUSE 0x8000 // replace with the link_out phoneme if the next phoneme is a pause
-#define phALTERNATIVE 0x1c00 // bits 10,11,12 specifying use of alternative_ph
-#define phBEFOREVOWEL 0x0000
-#define phBEFOREVOWELPAUSE 0x0400
-#define phBEFORENOTVOWEL 0x0c00
-#define phBEFORENOTVOWEL2 0x1000
-#define phSWITCHVOICING 0x0800
-#define phBEFORE_R 0x1400
-
-#define phNONSYLLABIC 0x100000 // don't count this vowel as a syllable when finding the stress position
+#define phNONSYLLABIC 0x100000 // don't count this vowel as a syllable when finding the stress position
#define phLONG 0x200000
#define phLENGTHENSTOP 0x400000 // make the pre-pause slightly longer
-#define phRHOTIC 0x800000
+#define phRHOTIC 0x800000 // bit 23
+#define phNOPAUSE 0x1000000
+#define phPREVOICE 0x2000000 // for voiced stops
+
+#define phFLAG1 0x10000000
+#define phFLAG2 0x20000000
+#define phFLAG3 0x40000000
+#define phLOCAL 0x80000000 // used during compilation
// fixed phoneme code numbers, these can be used from the program code
#define phonCONTROL 1
@@ -78,7 +77,6 @@
#define phonSCHWA 13
#define phonSCHWA_SHORT 14
#define phonEND_WORD 15
-#define phonSONORANT 16
#define phonDEFAULTTONE 17
#define phonCAPITAL 18
#define phonGLOTTALSTOP 19
@@ -90,11 +88,13 @@
#define phonT_REDUCED 25
#define phonSTRESS_TONIC 26
#define phonPAUSE_CLAUSE 27
+#define phonVOWELTYPES 28 // 28 to 33
extern const unsigned char pause_phonemes[8]; // 0, vshort, short, pause, long, glottalstop
// place of articulation
#define phPLACE 0xf0000
+#define phPLACE_blb 0x10000
#define phPLACE_pla 0x60000
#define N_PHONEME_TABS 100 // number of phoneme tables
@@ -102,29 +102,22 @@ extern const unsigned char pause_phonemes[8]; // 0, vshort, short, pause, long,
#define N_PHONEME_TAB_NAME 32 // must be multiple of 4
// main table of phonemes, index by phoneme number (1-254)
-typedef struct {
- unsigned int mnemonic; // 1st char is in the l.s.byte
- unsigned int phflags; // bits 28-30 reduce_to level, bits 16-19 place of articulation
- // bits 10-11 alternative ph control
- unsigned short std_length; // for vowels, in mS; for phSTRESS, the stress/tone type
- unsigned short spect;
- unsigned short before;
- unsigned short after;
-
- unsigned char code; // the phoneme number
- unsigned char type; // phVOWEL, phPAUSE, phSTOP etc
+typedef struct {
+ unsigned int mnemonic; // Up to 4 characters. The first char is in the l.s.byte
+ unsigned int phflags; // bits 16-19 place of articulation
+ unsigned short program; // index into phondata file
+ unsigned char code; // the phoneme number
+ unsigned char type; // phVOWEL, phPAUSE, phSTOP etc
unsigned char start_type;
unsigned char end_type;
-
- unsigned char length_mod; // a length_mod group number, used to access length_mod_tab
- unsigned char reduce_to; // change to this phoneme if unstressed
- unsigned char alternative_ph; // change to this phoneme if a vowel follows/doesn't follow
- unsigned char link_out; // insert linking phoneme if a vowel follows
-
+ unsigned char std_length; // for vowels, in mS/2; for phSTRESS phonemes, this is the stress/tone type
+ unsigned char length_mod; // a length_mod group number, used to access length_mod_tab
+
} PHONEME_TAB;
+
// Several phoneme tables may be loaded into memory. phoneme_tab points to
// one for the current voice
extern int n_phoneme_tab;
@@ -136,7 +129,8 @@ typedef struct {
char name[N_PHONEME_TAB_NAME];
PHONEME_TAB *phoneme_tab_ptr;
int n_phonemes;
- int includes; // also include the phonemes from this other phoneme table
+ int includes; // also include the phonemes from this other phoneme table
+ int equivalence_tables; // lists of equivalent phonemes to match other languages, byte index into phondata
} PHONEME_TAB_LIST;
@@ -153,13 +147,21 @@ extern int n_replace_phonemes;
extern REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES];
-#define PH(c1,c2) (c2<<8)+c1 // combine two characters into an integer for phoneme name
+// Table of phoneme programs and lengths. Used by MakeVowelLists
+typedef struct {
+ unsigned int addr;
+ unsigned int length;
+} PHONEME_PROG_LOG;
+
+
+
+#define PH(c1,c2) (c2<<8)+c1 // combine two characters into an integer for phoneme name
#define PH3(c1,c2,c3) (c3<<16)+(c2<<8)+c1
#define PhonemeCode2(c1,c2) PhonemeCode((c2<<8)+c1)
int LookupPhonemeString(const char *string);
int PhonemeCode(unsigned int mnem);
-char *EncodePhonemes(char *p, char *outptr, unsigned char *bad_phoneme);
+const char *EncodePhonemes(const char *p, char *outptr, int *bad_phoneme);
void DecodePhonemes(const char *inptr, char *outptr);
extern const char *WordToString(unsigned int word);
diff --git a/navit/support/espeak/phonemelist.c b/navit/support/espeak/phonemelist.c
index d663e2e94..07d6fd3d7 100755..100644
--- a/navit/support/espeak/phonemelist.c
+++ b/navit/support/espeak/phonemelist.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -38,20 +38,8 @@ extern PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes
-static int ChangePhonemes(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
-{//=================================================================================================================
-// Called for each phoneme in the phoneme list, to allow a language to make changes
-// ph The current phoneme
-
- if(tr->translator_name == L('r','u'))
- return(ChangePhonemes_ru(tr, phlist, n_ph, index, ph, ch));
-
- return(0);
-}
-
-
-static int SubstitutePhonemes(Translator *tr, PHONEME_LIST2 *plist_out)
-{//====================================================================
+static int SubstitutePhonemes(Translator *tr, PHONEME_LIST *plist_out)
+{//===================================================================
// Copy the phonemes list and perform any substitutions that are required for the
// current voice
int ix;
@@ -59,113 +47,47 @@ static int SubstitutePhonemes(Translator *tr, PHONEME_LIST2 *plist_out)
int replace_flags;
int n_plist_out = 0;
int word_end;
- int max_stress = -1;
- int switched_language = 0;
- int max_stress_posn=0;
- int n_syllables = 0;
- int syllable = 0;
- int syllable_stressed = 0;
PHONEME_LIST2 *plist2;
- PHONEME_LIST2 *pl;
PHONEME_TAB *next=NULL;
for(ix=0; (ix < n_ph_list2) && (n_plist_out < N_PHONEME_LIST); ix++)
{
plist2 = &ph_list2[ix];
- if(plist2->phcode == phonSWITCH)
- switched_language ^= 1;
-
// don't do any substitution if the language has been temporarily changed
- if(switched_language == 0)
+ if(!(plist2->synthflags & SFLAG_SWITCHED_LANG))
{
if(ix < (n_ph_list2 -1))
next = phoneme_tab[ph_list2[ix+1].phcode];
-
+
word_end = 0;
if((plist2+1)->sourceix || ((next != 0) && (next->type == phPAUSE)))
word_end = 1; // this phoneme is the end of a word
-
- if(tr->langopts.phoneme_change != 0)
- {
- // this language does changes to phonemes after translation
- int flags;
- CHANGEPH ch;
- if(plist2->sourceix)
- {
- // start of a word, find the stressed vowel
- syllable = 0;
- syllable_stressed = 0;
- n_syllables = 0;
-
- max_stress = -1;
- max_stress_posn = ix;
- for(k=ix; k < n_ph_list2; k++)
- {
- if(((pl = &ph_list2[k])->sourceix != 0) && (k > ix))
- break;
-
- pl->stress &= 0xf;
-
- if(phoneme_tab[pl->phcode]->type == phVOWEL)
- {
- n_syllables++;
-
- if(pl->stress > max_stress)
- {
- syllable_stressed = n_syllables;
- max_stress = pl->stress;
- max_stress_posn = k;
- }
- }
- }
- }
- if(phoneme_tab[plist2->phcode]->type == phVOWEL)
- {
- syllable++;
- }
-
- // make any language specific changes
- flags = 0;
- if(ix == max_stress_posn)
- flags |= 2;
- if(ix > max_stress_posn)
- flags |= 4;
- if(ph_list2[ix].synthflags & SFLAG_DICTIONARY)
- flags |= 8;
- ch.flags = flags | word_end;
-
- ch.stress = plist2->stress;
- ch.stress_highest = max_stress;
- ch.n_vowels = n_syllables;
- ch.vowel_this = syllable;
- ch.vowel_stressed = syllable_stressed;
-
- ChangePhonemes(tr, ph_list2, n_ph_list2, ix, phoneme_tab[ph_list2[ix].phcode], &ch);
- }
-
// check whether a Voice has specified that we should replace this phoneme
for(k=0; k<n_replace_phonemes; k++)
{
if(plist2->phcode == replace_phonemes[k].old_ph)
{
replace_flags = replace_phonemes[k].type;
-
+
if((replace_flags & 1) && (word_end == 0))
continue; // this replacement only occurs at the end of a word
-
- if((replace_flags & 2) && ((plist2->stress & 0x7) > 3))
+
+ if((replace_flags & 2) && ((plist2->stresslevel & 0x7) > 3))
continue; // this replacement doesn't occur in stressed syllables
-
+
+ if((replace_flags & 4) && (plist2->sourceix == 0))
+ continue; // this replacement only occurs at the start of a word
+
// substitute the replacement phoneme
plist2->phcode = replace_phonemes[k].new_ph;
- if((plist2->stress > 1) && (phoneme_tab[plist2->phcode]->phflags & phUNSTRESSED))
- plist2->stress = 0; // the replacement must be unstressed
+ if((plist2->stresslevel > 1) && (phoneme_tab[plist2->phcode]->phflags & phUNSTRESSED))
+ plist2->stresslevel = 0; // the replacement must be unstressed
break;
}
}
-
+
if(plist2->phcode == 0)
{
continue; // phoneme has been replaced by NULL, so don't copy it
@@ -173,7 +95,10 @@ static int SubstitutePhonemes(Translator *tr, PHONEME_LIST2 *plist_out)
}
// copy phoneme into the output list
- memcpy(&plist_out[n_plist_out++],plist2,sizeof(PHONEME_LIST2));
+ memcpy(&plist_out[n_plist_out],plist2,sizeof(PHONEME_LIST2));
+ plist_out[n_plist_out].ph = phoneme_tab[plist2->phcode];
+ plist_out[n_plist_out].type = plist_out[n_plist_out].ph->type;
+ n_plist_out++;
}
return(n_plist_out);
} // end of SubstitutePhonemes
@@ -188,22 +113,30 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
int insert_ph = 0;
PHONEME_LIST *phlist;
PHONEME_TAB *ph;
- PHONEME_TAB *prev, *next, *next2;
+ PHONEME_TAB *next, *next2;
int unstress_count = 0;
int word_stress = 0;
- int switched_language = 0;
+ int current_phoneme_tab;
int max_stress;
int voicing;
int regression;
int end_sourceix;
int alternative;
- int first_vowel=0; // first vowel in a word
- PHONEME_LIST2 ph_list3[N_PHONEME_LIST];
+ int delete_count;
+ int word_start;
+ int inserted;
+ int deleted;
+ PHONEME_DATA phdata;
- static PHONEME_LIST2 ph_list2_null = {0,0,0,0,0};
- PHONEME_LIST2 *plist2 = &ph_list2_null;
- PHONEME_LIST2 *plist2_inserted = NULL;
+ int n_ph_list3;
+ PHONEME_LIST *plist3;
+ PHONEME_LIST *plist3_inserted = NULL;
+ PHONEME_LIST ph_list3[N_PHONEME_LIST];
+ PHONEME_LIST2 *plist2;
+ WORD_PH_DATA worddata;
+
+ memset(&worddata, 0, sizeof(worddata));
plist2 = ph_list2;
phlist = phoneme_list;
end_sourceix = plist2[n_ph_list2-1].sourceix;
@@ -212,9 +145,9 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
max_stress = 0;
for(j = n_ph_list2-3; j>=0; j--)
{
- // start with the last phoneme (before the terminating pauses) and move forwards
- if((plist2[j].stress & 0x7f) > max_stress)
- max_stress = plist2[j].stress & 0x7f;
+ // start with the last phoneme (before the terminating pauses) and move backwards
+ if((plist2[j].stresslevel & 0x7f) > max_stress)
+ max_stress = plist2[j].stresslevel & 0x7f;
if(plist2[j].sourceix != 0)
break;
}
@@ -225,10 +158,10 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
{
if(plist2[j].synthflags & SFLAG_PROMOTE_STRESS) // dictionary flags indicated that this stress can be promoted
{
- plist2[j].stress = 4; // promote to stressed
+ plist2[j].stresslevel = 4; // promote to stressed
break;
}
- if(plist2[j].stress >= 4)
+ if(plist2[j].stresslevel >= 4)
{
// found a stressed syllable, so stop looking
break;
@@ -236,11 +169,48 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
}
}
+ // look for switch of phoneme tables
+ delete_count = 0;
+ current_phoneme_tab = tr->phoneme_tab_ix;
+ for(j = 0; j < n_ph_list2; j++)
+ {
+ if(current_phoneme_tab != tr->phoneme_tab_ix)
+ {
+ plist2[j].synthflags |= SFLAG_SWITCHED_LANG;
+ }
+
+ if(delete_count > 0)
+ {
+ memcpy(&plist2[j-delete_count], &plist2[j], sizeof(plist2[0]));
+ }
+
+ if(plist2[j].phcode == phonSWITCH)
+ {
+ if((!(plist2[j].synthflags & SFLAG_EMBEDDED)) && (
+ (plist2[j].tone_ph == current_phoneme_tab) ||
+ (plist2[j+1].phcode == phonSWITCH) ||
+ ((plist2[j+1].phcode == phonPAUSE) && (plist2[j+2].phcode == phonSWITCH))
+ ))
+ {
+ // delete this phonSWITCH if it's switching to the current phoneme table, or
+ // delete this phonSWITCH if its followed by another phonSWITCH
+ delete_count++;
+ }
+ else
+ {
+ current_phoneme_tab = plist2[j].tone_ph;
+ }
+ }
+
+ }
+ n_ph_list2 -= delete_count;
+
if((regression = tr->langopts.param[LOPT_REGRESSIVE_VOICING]) != 0)
{
// set consonant clusters to all voiced or all unvoiced
// Regressive
int type;
+ int stop_propagation = 0;
voicing = 0;
for(j=n_ph_list2-1; j>=0; j--)
@@ -249,43 +219,48 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
if(ph == NULL)
continue;
- if(ph->code == phonSWITCH)
- switched_language ^= 1;
- if(switched_language)
+ if(plist2[j].synthflags & SFLAG_SWITCHED_LANG)
+ {
+ stop_propagation = 0;
+ voicing = 0;
+ if(regression & 0x100)
+ voicing = 1; // word-end devoicing
continue;
+ }
type = ph->type;
if(regression & 0x2)
{
- // LANG=Russian, [v] amd [v;] don't cause regression, or [R^]
- if((ph->mnemonic == 'v') || (ph->mnemonic == ((';'<<8)+'v')) || ((ph->mnemonic & 0xff)== 'R'))
- type = phLIQUID;
+ // [v] amd [v;] don't cause regression, or [R^]
+ if(((ph->mnemonic & 0xff) == 'v') || ((ph->mnemonic & 0xff)== 'R'))
+ {
+ stop_propagation = 1;
+ if(regression & 0x10)
+ voicing = 0;
+ }
}
if((type==phSTOP) || type==(phFRICATIVE))
{
- if(voicing==0)
+ if((voicing==0) && (regression & 0xf))
{
voicing = 1;
}
- else
- if((voicing==2) && ((ph->phflags & phALTERNATIVE)==phSWITCHVOICING))
+ else if((voicing==2) && (ph->end_type != 0)) // use end_type field for voicing_switch for consonants
{
- plist2[j].phcode = ph->alternative_ph; // change to voiced equivalent
+ plist2[j].phcode = ph->end_type; // change to voiced equivalent
}
}
- else
- if((type==phVSTOP) || type==(phVFRICATIVE))
+ else if((type==phVSTOP) || type==(phVFRICATIVE))
{
- if(voicing==0)
+ if((voicing==0) && (regression & 0xf))
{
voicing = 2;
}
- else
- if((voicing==1) && ((ph->phflags & phALTERNATIVE)==phSWITCHVOICING))
+ else if((voicing==1) && (ph->end_type != 0))
{
- plist2[j].phcode = ph->alternative_ph; // change to unvoiced equivalent
+ plist2[j].phcode = ph->end_type; // change to unvoiced equivalent
}
}
else
@@ -301,234 +276,257 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
voicing = 0;
}
}
- if((regression & 0x4) && (plist2[j].sourceix))
+ if(stop_propagation)
{
- // stop propagation at a word boundary
voicing = 0;
+ stop_propagation = 0;
+ }
+
+ if(plist2[j].sourceix)
+ {
+ if(regression & 0x04)
+ {
+ // stop propagation at a word boundary
+ voicing = 0;
+ }
+ if(regression & 0x100)
+ {
+ // devoice word-final consonants, unless propagating voiced
+ if(voicing == 0)
+ {
+ voicing = 1;
+ }
+ }
}
}
}
- n_ph_list2 = SubstitutePhonemes(tr,ph_list3) - 2;
+ n_ph_list3 = SubstitutePhonemes(tr,ph_list3) - 2;
+
+ for(j=0; (j < n_ph_list3) && (ix < N_PHONEME_LIST-3);)
+ {
+ if(ph_list3[j].sourceix)
+ {
+ // start of a word
+ int k;
+ int nextw;
+ word_stress = 0;
+
+ // find the highest stress level in this word
+ for(nextw=j; nextw < n_ph_list3;)
+ {
+ if(ph_list3[nextw].stresslevel > word_stress)
+ word_stress = ph_list3[nextw].stresslevel;
+
+ nextw++;
+ if(ph_list3[nextw].sourceix)
+ break; // start of the next word
+ }
+ for(k=j; k<nextw; k++)
+ {
+ ph_list3[k].wordstress = word_stress;
+ }
+ j = nextw;
+ }
+ else
+ {
+ j++;
+ }
+ }
// transfer all the phonemes of the clause into phoneme_list
ph = phoneme_tab[phonPAUSE];
- switched_language = 0;
+ ph_list3[0].ph = ph;
+ word_start = 1;
- for(j=0; insert_ph || ((j < n_ph_list2) && (ix < N_PHONEME_LIST-3)); j++)
+ for(j=0; insert_ph || ((j < n_ph_list3) && (ix < N_PHONEME_LIST-3)); j++)
{
- prev = ph;
-
- plist2 = &ph_list3[j];
+ plist3 = &ph_list3[j];
+ inserted = 0;
+ deleted = 0;
if(insert_ph != 0)
{
// we have a (linking) phoneme which we need to insert here
- next = phoneme_tab[plist2->phcode]; // this phoneme, i.e. after the insert
+ next = phoneme_tab[plist3->phcode]; // this phoneme, i.e. after the insert
// re-use the previous entry for the inserted phoneme.
- // That's OK because we don't look backwards from plist2
+ // That's OK because we don't look backwards from plist3 *** but CountVowelPosition() and isAfterStress does !!!
j--;
- plist2 = plist2_inserted = &ph_list3[j];
- memset(plist2, 0, sizeof(*plist2));
- plist2->phcode = insert_ph;
+ plist3 = plist3_inserted = &ph_list3[j];
+ if(j > 0)
+ {
+ // move all previous phonemes in the word back one place
+ int k;
+ if(word_start > 0)
+ {
+ k = word_start;
+ word_start--;
+ }
+ else
+ {
+ k = 2; // No more space, don't loose the start of word mark at ph_list2[word_start]
+ }
+ for(; k<=j; k++)
+ memcpy(&ph_list3[k-1], &ph_list3[k], sizeof(*plist3));
+ }
+ memset(&plist3[0], 0, sizeof(*plist3));
+ plist3->phcode = insert_ph;
ph = phoneme_tab[insert_ph];
+ plist3->ph = ph;
insert_ph = 0;
+ inserted = 1; // don't insert the same phoneme repeatedly
}
else
{
// otherwise get the next phoneme from the list
- ph = phoneme_tab[plist2->phcode];
+ if(plist3->sourceix != 0)
+ word_start = j;
- if(plist2->phcode == phonSWITCH)
- {
- // change phoneme table
- SelectPhonemeTable(plist2->tone_number);
- switched_language ^= SFLAG_SWITCHED_LANG;
- }
- next = phoneme_tab[(plist2+1)->phcode]; // the phoneme after this one
- }
+ ph = phoneme_tab[plist3->phcode];
+ plist3[0].ph = ph;
- if(plist2->sourceix)
- {
- // start of a word
- int k;
- word_stress = 0;
- first_vowel = 1;
-
- // find the highest stress level in this word
- for(k=j+1; k < n_ph_list2; k++)
+ if(plist3->phcode == phonSWITCH)
{
- if(ph_list3[k].sourceix)
- break; // start of the next word
-
- if(ph_list3[k].stress > word_stress)
- word_stress = ph_list3[k].stress;
+ // change phoneme table
+ SelectPhonemeTable(plist3->tone_ph);
}
+ next = phoneme_tab[plist3[1].phcode]; // the phoneme after this one
+ plist3[1].ph = next;
}
if(ph == NULL) continue;
- if(ph->type == phVOWEL)
- {
- // check for consecutive unstressed syllables
- if(plist2->stress == 0)
- {
- // an unstressed vowel
- unstress_count++;
- if((unstress_count > 1) && ((unstress_count & 1)==0))
- {
- // in a sequence of unstressed syllables, reduce alternate syllables to 'diminished'
- // stress. But not for the last phoneme of a stressed word
- if((tr->langopts.stress_flags & 0x2) || ((word_stress > 3) && ((plist2+1)->sourceix!=0)))
- {
- // An unstressed final vowel of a stressed word
- unstress_count=1; // try again for next syllable
- }
- else
- {
- plist2->stress = 1; // change stress to 'diminished'
- }
- }
- }
- else
- {
- unstress_count = 0;
- }
- }
-
- alternative = 0;
-
- if(ph->alternative_ph > 0)
- {
- switch(ph->phflags & phALTERNATIVE)
- {
- // This phoneme changes if vowel follows, or doesn't follow, depending on its phNOTFOLLOWS flag
- case phBEFORENOTVOWEL:
- if(next->type != phVOWEL)
- alternative = ph->alternative_ph;
- break;
-
- case phBEFORENOTVOWEL2: // LANG=tr
- if(((plist2+1)->sourceix != 0) ||
- ((next->type != phVOWEL) && ((phoneme_tab[(plist2+2)->phcode]->type != phVOWEL) || ((plist2+2)->sourceix != 0))))
- {
- alternative = ph->alternative_ph;
- }
- break;
-
- case phBEFOREVOWELPAUSE:
- if((next->type == phVOWEL) || (next->type == phPAUSE))
- alternative = ph->alternative_ph;
- break;
-
- case phBEFOREVOWEL:
- if(next->type == phVOWEL)
- alternative = ph->alternative_ph;
- break;
+ InterpretPhoneme(tr, 0x100, plist3, &phdata, &worddata);
- case phBEFORE_R:
- if(next->phflags & phRHOTIC)
- {
- alternative = ph->alternative_ph;
- }
- break;
- }
- }
- if(ph->phflags & phBEFOREPAUSE)
+ if((alternative = phdata.pd_param[pd_CHANGE_NEXTPHONEME]) > 0)
{
- if(next->type == phPAUSE)
- alternative = ph->link_out; // replace with the link_out phoneme
+ ph_list3[j+1].ph = phoneme_tab[alternative];
+ ph_list3[j+1].phcode = alternative;
+ ph_list3[j+1].type = phoneme_tab[alternative]->type;
+ next = phoneme_tab[alternative];
}
- if(alternative == 1)
- continue; // NULL phoneme, discard
-
- if(alternative > 1)
+ if(((alternative = phdata.pd_param[pd_INSERTPHONEME]) > 0) && (inserted == 0))
{
+ // PROBLEM: if we insert a phoneme before a vowel then we loose the stress.
PHONEME_TAB *ph2;
ph2 = ph;
+
+ insert_ph = plist3->phcode;
ph = phoneme_tab[alternative];
+ plist3->ph = ph;
+ plist3->phcode = alternative;
if(ph->type == phVOWEL)
{
- plist2->synthflags |= SFLAG_SYLLABLE;
+ plist3->synthflags |= SFLAG_SYLLABLE;
if(ph2->type != phVOWEL)
- plist2->stress = 0; // change from non-vowel to vowel, make sure it's unstressed
+ plist3->stresslevel = 0; // change from non-vowel to vowel, make sure it's unstressed
}
else
- plist2->synthflags &= ~SFLAG_SYLLABLE;
- }
+ plist3->synthflags &= ~SFLAG_SYLLABLE;
- if(tr->langopts.param[LOPT_REDUCE_T])
- {
- if((ph->mnemonic == 't') && (plist2->sourceix == 0) && ((prev->type == phVOWEL) || (prev->mnemonic == 'n')))
- {
- if(((plist2+1)->sourceix == 0) && ((plist2+1)->stress < 3) && (next->type == phVOWEL))
- {
- ph = phoneme_tab[phonT_REDUCED];
- }
- }
+ // re-interpret the changed phoneme
+ // But it doesn't obey a second ChangePhoneme()
+ InterpretPhoneme(tr, 0x100, plist3, &phdata, &worddata);
}
-
- while((ph->reduce_to != 0) && (!(plist2->synthflags & SFLAG_DICTIONARY) || (tr->langopts.param[LOPT_REDUCE] & 1)))
+ if((alternative = phdata.pd_param[pd_CHANGEPHONEME]) > 0)
{
- int reduce_level;
- int stress_level;
- int reduce = 0;
-
- reduce_level = (ph->phflags >> 28) & 7;
+ PHONEME_TAB *ph2;
+ ph2 = ph;
+ ph = phoneme_tab[alternative];
+ plist3->ph = ph;
+ plist3->phcode = alternative;
- if(ph->type == phVOWEL)
+ if(alternative == 1)
{
- stress_level = plist2->stress;
+ deleted = 1; // NULL phoneme, discard
}
else
{
- // consonant, get stress from the following vowel
- if(next->type == phVOWEL)
- stress_level = (plist2+1)->stress;
+ if(ph->type == phVOWEL)
+ {
+ plist3->synthflags |= SFLAG_SYLLABLE;
+ if(ph2->type != phVOWEL)
+ plist3->stresslevel = 0; // change from non-vowel to vowel, make sure it's unstressed
+ }
else
- break;
- }
+ plist3->synthflags &= ~SFLAG_SYLLABLE;
- if((stress_level == 1) && (first_vowel))
- stress_level = 0; // ignore 'dimished' stress on first syllable
-
- if(stress_level == 1)
- reduce = 1; // stress = 'reduced'
+ // re-interpret the changed phoneme
+ // But it doesn't obey a second ChangePhoneme()
+ InterpretPhoneme(tr, 0x100, plist3, &phdata, &worddata);
+ }
+ }
- if(stress_level < reduce_level)
- reduce =1;
+ if((ph->type == phVOWEL) && (deleted == 0))
+ {
+ PHONEME_LIST *p;
- if((word_stress < 4) && (tr->langopts.param[LOPT_REDUCE] & 0x2) && (stress_level >= word_stress))
+ // Check for consecutive unstressed syllables, even across word boundaries.
+ // Do this after changing phonemes according to stress level.
+ if(plist3->stresslevel <= 1)
{
- // don't reduce the most stressed syllable in an unstressed word
- reduce = 0;
- }
+ // an unstressed vowel
+ unstress_count++;
- if(reduce)
- ph = phoneme_tab[ph->reduce_to];
+ if(tr->langopts.stress_flags & 0x08)
+ {
+ // change sequences of consecutive unstressed vowels in unstressed words to diminished stress (TEST)
+ for(p=plist3+1; p->type != phPAUSE; p++)
+ {
+ if(p->type == phVOWEL)
+ {
+ if(p->stresslevel <= 1)
+ {
+ if(plist3->wordstress < 4)
+ plist3->stresslevel = 0;
+ if(p->wordstress < 4)
+ p->stresslevel = 0;
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ if((unstress_count > 1) && ((unstress_count & 1)==0))
+ {
+ // in a sequence of unstressed syllables, reduce alternate syllables to 'diminished'
+ // stress. But not for the last phoneme of a stressed word
+ if((tr->langopts.stress_flags & S_NO_DIM) || ((word_stress > 3) && ((plist3+1)->sourceix!=0)))
+ {
+ // An unstressed final vowel of a stressed word
+ unstress_count=1; // try again for next syllable
+ }
+ else
+ {
+ plist3->stresslevel = 0; // change stress to 'diminished'
+ }
+ }
+ }
+ }
else
- break;
+ {
+ unstress_count = 0;
+ }
}
- if(ph->type == phVOWEL)
- first_vowel = 0;
-
- if((plist2+1)->synthflags & SFLAG_LENGTHEN)
+ if((plist3+1)->synthflags & SFLAG_LENGTHEN)
{
static char types_double[] = {phFRICATIVE,phVFRICATIVE,phNASAL,phLIQUID,0};
- if(strchr(types_double,next->type))
+ if((j > 0) && (strchr(types_double,next->type)))
{
// lengthen this consonant by doubling it
+ // BUT, can't insert a phoneme at position plist3[0] because it crashes PrevPh()
insert_ph = next->code;
- (plist2+1)->synthflags ^= SFLAG_LENGTHEN;
+ (plist3+1)->synthflags ^= SFLAG_LENGTHEN;
}
}
- if((plist2+1)->sourceix != 0)
+ if((plist3+1)->sourceix != 0)
{
int x;
@@ -551,7 +549,7 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
else
insert_ph = phonPAUSE_VSHORT;
}
-
+
if((ph->type == phVOWEL) && ((x = tr->langopts.vowel_pause & 0x03) != 0))
{
// adjacent vowels over a word boundary
@@ -560,8 +558,8 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
else
insert_ph = phonPAUSE_VSHORT;
}
-
- if(((plist2+1)->stress >= 4) && (tr->langopts.vowel_pause & 0x100))
+
+ if(((plist3+1)->stresslevel >= 4) && (tr->langopts.vowel_pause & 0x100))
{
// pause before a words which starts with a stressed vowel
insert_ph = phonPAUSE_SHORT;
@@ -569,7 +567,7 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
}
}
- if(plist2 != plist2_inserted)
+ if((plist3 != plist3_inserted) && (ix > 0))
{
if((x = (tr->langopts.word_gap & 0x7)) != 0)
{
@@ -586,33 +584,12 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
}
}
- next2 = phoneme_tab[(plist2+2)->phcode];
+ next2 = phoneme_tab[plist3[2].phcode];
+ plist3[2].ph = next2;
- if((insert_ph == 0) && (ph->link_out != 0) && !(ph->phflags & phBEFOREPAUSE) && (((plist2+1)->synthflags & SFLAG_EMBEDDED)==0))
+ if((insert_ph == 0) && (phdata.pd_param[pd_APPENDPHONEME] != 0))
{
- if(ph->phflags & phAPPENDPH)
- {
- // always append the specified phoneme, unless it already is the next phoneme
- if((ph->link_out != (plist2+1)->phcode) && (next->type == phVOWEL))
-// if(ph->link_out != (plist2+1)->phcode)
- {
- insert_ph = ph->link_out;
- }
- }
- else
- if(((tr->langopts.word_gap & 8)==0) || ((plist2+1)->sourceix == 0))
- {
- // This phoneme can be linked to a following vowel by inserting a linking phoneme
- if(next->type == phVOWEL)
- insert_ph = ph->link_out;
- else
- if(next->code == phonPAUSE_SHORT)
- {
- // Pause followed by Vowel, replace the Short Pause with the linking phoneme,
- if(next2->type == phVOWEL)
- (plist2+1)->phcode = ph->link_out; // replace pause by linking phoneme
- }
- }
+ insert_ph = phdata.pd_param[pd_APPENDPHONEME];
}
if(ph->phflags & phVOICED)
@@ -623,62 +600,70 @@ void MakePhonemeList(Translator *tr, int post_pause, int start_sentence)
// not yet implemented
}
- phlist[ix].ph = ph;
- phlist[ix].type = ph->type;
- phlist[ix].env = PITCHfall; // default, can be changed in the "intonation" module
- phlist[ix].synthflags = plist2->synthflags | switched_language;
- phlist[ix].stresslevel = plist2->stress & 0xf;
- phlist[ix].tone_ph = plist2->tone_number;
- phlist[ix].sourceix = 0;
-
- if(plist2->sourceix != 0)
+ if(deleted == 0)
{
- phlist[ix].sourceix = plist2->sourceix;
- phlist[ix].newword = 1; // this phoneme is the start of a word
+ phlist[ix].ph = ph;
+ phlist[ix].type = ph->type;
+ phlist[ix].env = PITCHfall; // default, can be changed in the "intonation" module
+ phlist[ix].synthflags = plist3->synthflags;
+ phlist[ix].stresslevel = plist3->stresslevel & 0xf;
+ phlist[ix].wordstress = plist3->wordstress;
+ phlist[ix].tone_ph = plist3->tone_ph;
+ phlist[ix].sourceix = 0;
+ phlist[ix].phcode = ph->code;
+
+ if(plist3->sourceix != 0)
+ {
+ phlist[ix].sourceix = plist3->sourceix;
+ phlist[ix].newword = 1; // this phoneme is the start of a word
- if(start_sentence)
+ if(start_sentence)
+ {
+ phlist[ix].newword = 5; // start of sentence + start of word
+ start_sentence = 0;
+ }
+ }
+ else
{
- phlist[ix].newword = 5; // start of sentence + start of word
- start_sentence = 0;
+ phlist[ix].newword = 0;
}
- }
- else
- {
- phlist[ix].newword = 0;
- }
- phlist[ix].length = ph->std_length;
- if((ph->code == phonPAUSE_LONG) && (option_wordgap > 0))
- {
- phlist[ix].ph = phoneme_tab[phonPAUSE_SHORT];
- phlist[ix].length = option_wordgap*14; // 10mS per unit at the default speed
- }
+ // phlist[ix].length = ph->std_length;
+ phlist[ix].length = phdata.pd_param[i_SET_LENGTH]*2;
+ if((ph->code == phonPAUSE_LONG) && (option_wordgap > 0) && (plist3[1].sourceix != 0))
+ {
+ phlist[ix].ph = phoneme_tab[phonPAUSE_SHORT];
+ phlist[ix].length = option_wordgap*14; // 10mS per unit at the default speed
+ }
- if(ph->type==phVOWEL || ph->type==phLIQUID || ph->type==phNASAL || ph->type==phVSTOP || ph->type==phVFRICATIVE)
- {
- phlist[ix].length = 128; // length_mod
- phlist[ix].env = PITCHfall;
- }
+ if(ph->type==phVOWEL || ph->type==phLIQUID || ph->type==phNASAL || ph->type==phVSTOP || ph->type==phVFRICATIVE || (ph->phflags & phPREVOICE))
+ {
+ phlist[ix].length = 128; // length_mod
+ phlist[ix].env = PITCHfall;
+ }
- phlist[ix].prepause = 0;
- phlist[ix].amp = 20; // default, will be changed later
- phlist[ix].pitch1 = 0x400;
- phlist[ix].pitch2 = 0x400;
- ix++;
+ phlist[ix].prepause = 0;
+ phlist[ix].amp = 20; // default, will be changed later
+ phlist[ix].pitch1 = 255;
+ phlist[ix].pitch2 = 255;
+ ix++;
+ }
}
phlist[ix].newword = 2; // end of clause
- phlist[ix].type = phPAUSE; // terminate with 2 Pause phonemes
+ phlist[ix].phcode = phonPAUSE;
+ phlist[ix].type = phPAUSE; // terminate with 2 Pause phonemes
phlist[ix].length = post_pause; // length of the pause, depends on the punctuation
phlist[ix].sourceix = end_sourceix;
phlist[ix].synthflags = 0;
+ phlist[ix++].ph = phoneme_tab[phonPAUSE];
- phlist[ix++].ph = phoneme_tab[phonPAUSE];
- phlist[ix].type = phPAUSE;
+ phlist[ix].phcode = phonPAUSE;
+ phlist[ix].type = phPAUSE;
phlist[ix].length = 0;
phlist[ix].sourceix=0;
phlist[ix].synthflags = 0;
- phlist[ix++].ph = phoneme_tab[phonPAUSE_SHORT];
+ phlist[ix++].ph = phoneme_tab[phonPAUSE_SHORT];
n_phoneme_list = ix;
} // end of MakePhonemeList
diff --git a/navit/support/espeak/portaudio.h b/navit/support/espeak/portaudio.h
index 2ab8e02a4..2ab8e02a4 100755..100644
--- a/navit/support/espeak/portaudio.h
+++ b/navit/support/espeak/portaudio.h
diff --git a/navit/support/espeak/portaudio18.h b/navit/support/espeak/portaudio18.h
index 2ab8e02a4..2ab8e02a4 100755..100644
--- a/navit/support/espeak/portaudio18.h
+++ b/navit/support/espeak/portaudio18.h
diff --git a/navit/support/espeak/readclause.c b/navit/support/espeak/readclause.c
index ba4071c1a..172ebad15 100644
--- a/navit/support/espeak/readclause.c
+++ b/navit/support/espeak/readclause.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -41,7 +41,7 @@
#include <locale.h>
#define N_XML_BUF 256
-#define double(x) ((double)(x))
+
static const char *xmlbase = ""; // base URL from <speak>
static int namedata_ix=0;
@@ -58,20 +58,21 @@ static const char *ungot_word = NULL;
static int end_of_input;
static int ignore_text=0; // set during <sub> ... </sub> to ignore text which has been replaced by an alias
+static int audio_text=0; // set during <audio> ... </audio>
static int clear_skipping_text = 0; // next clause should clear the skipping_text flag
int count_characters = 0;
static int sayas_mode;
+static int sayas_start;
static int ssml_ignore_l_angle = 0;
-static const char *punct_stop = ".:!?"; // pitch fall if followed by space
-static const char *punct_close = ")]}>;'\""; // always pitch fall unless followed by alnum
-
// alter tone for announce punctuation or capitals
-static const char *tone_punct_on = "\0016T"; // add reverberation, lower pitch
-static const char *tone_punct_off = "\001T";
+//static const char *tone_punct_on = "\0016T"; // add reverberation, lower pitch
+//static const char *tone_punct_off = "\001T\001P";
// punctuations symbols that can end a clause
static const unsigned short punct_chars[] = {',','.','?','!',':',';',
+ 0x00a1, // inverted exclamation
+ 0x00bf, // inverted question
0x2013, // en-dash
0x2014, // em-dash
0x2026, // elipsis
@@ -86,6 +87,15 @@ static const unsigned short punct_chars[] = {',','.','?','!',':',';',
0x055e, // Armenian question
0x055b, // Armenian emphasis mark
+ 0x060c, // Arabic ,
+ 0x061b, // Arabic ;
+ 0x061f, // Arabic ?
+ 0x06d4, // Arabic .
+
+ 0x0df4, // Singhalese Kunddaliya
+ 0x0f0d, // Tibet Shad
+ 0x0f0e,
+
0x1362, // Ethiopic period
0x1363,
0x1364,
@@ -93,6 +103,7 @@ static const unsigned short punct_chars[] = {',','.','?','!',':',';',
0x1366,
0x1367,
0x1368,
+ 0x10fb, // Georgian paragraph
0x3001, // ideograph comma
0x3002, // ideograph period
@@ -111,19 +122,30 @@ static const unsigned short punct_chars[] = {',','.','?','!',':',';',
// bits 0-7 pause x 10mS, bits 12-14 intonation type, bit 15 don't need following space or bracket
static const unsigned int punct_attributes [] = { 0,
CLAUSE_COMMA, CLAUSE_PERIOD, CLAUSE_QUESTION, CLAUSE_EXCLAMATION, CLAUSE_COLON, CLAUSE_SEMICOLON,
+ CLAUSE_SEMICOLON | 0x8000, // inverted exclamation
+ CLAUSE_SEMICOLON | 0x8000, // inverted question
CLAUSE_SEMICOLON, // en-dash
CLAUSE_SEMICOLON, // em-dash
- CLAUSE_SEMICOLON, // elipsis
+ CLAUSE_SEMICOLON | PUNCT_SAY_NAME | 0x8000, // elipsis
CLAUSE_QUESTION, // Greek question mark
CLAUSE_SEMICOLON, // Greek semicolon
- CLAUSE_PERIOD+0x8000, // Devanagari Danda (fullstop)
+ CLAUSE_PERIOD | 0x8000, // Devanagari Danda (fullstop)
- CLAUSE_PERIOD+0x8000, // Armenian period
+ CLAUSE_PERIOD | 0x8000, // Armenian period
CLAUSE_COMMA, // Armenian comma
- CLAUSE_EXCLAMATION + PUNCT_IN_WORD, // Armenian exclamation
- CLAUSE_QUESTION + PUNCT_IN_WORD, // Armenian question
- CLAUSE_PERIOD + PUNCT_IN_WORD, // Armenian emphasis mark
+ CLAUSE_EXCLAMATION | PUNCT_IN_WORD, // Armenian exclamation
+ CLAUSE_QUESTION | PUNCT_IN_WORD, // Armenian question
+ CLAUSE_PERIOD | PUNCT_IN_WORD, // Armenian emphasis mark
+
+ CLAUSE_COMMA, // Arabic ,
+ CLAUSE_SEMICOLON, // Arabic ;
+ CLAUSE_QUESTION, // Arabic question mark
+ CLAUSE_PERIOD, // Arabic full stop
+
+ CLAUSE_PERIOD+0x8000, // Singhalese period
+ CLAUSE_PERIOD+0x8000, // Tibet period
+ CLAUSE_PARAGRAPH,
CLAUSE_PERIOD, // Ethiopic period
CLAUSE_COMMA, // Ethiopic comma
@@ -131,7 +153,8 @@ static const unsigned int punct_attributes [] = { 0,
CLAUSE_COLON, // Ethiopic colon
CLAUSE_COLON, // Ethiopic preface colon
CLAUSE_QUESTION, // Ethiopic question mark
- CLAUSE_PERIOD, // Ethiopic paragraph
+ CLAUSE_PARAGRAPH, // Ethiopic paragraph
+ CLAUSE_PARAGRAPH, // Georgian paragraph
CLAUSE_COMMA+0x8000, // ideograph comma
CLAUSE_PERIOD+0x8000, // ideograph period
@@ -151,7 +174,7 @@ static const unsigned int punct_attributes [] = { 0,
// frame 0 is for the defaults, before any ssml tags.
typedef struct {
int tag_type;
- int voice_variant;
+ int voice_variant_number;
int voice_gender;
int voice_age;
char voice_name[40];
@@ -162,6 +185,8 @@ typedef struct {
static int n_ssml_stack;
static SSML_STACK ssml_stack[N_SSML_STACK];
+static espeak_VOICE base_voice;
+static char base_voice_variant_name[40] = {0};
static char current_voice_id[40] = {0};
@@ -170,10 +195,11 @@ static int n_param_stack;
PARAM_STACK param_stack[N_PARAM_STACK];
static int speech_parameters[N_SPEECH_PARAM]; // current values, from param_stack
+int saved_parameters[N_SPEECH_PARAM]; //Parameters saved on synthesis start
const int param_defaults[N_SPEECH_PARAM] = {
0, // silence (internal use)
- 170, // rate wpm
+ 175, // rate wpm
100, // volume
50, // pitch
50, // range
@@ -190,49 +216,90 @@ const int param_defaults[N_SPEECH_PARAM] = {
};
-#ifdef NEED_WCHAR_FUNCTIONS
+// additional Latin characters beyond the ascii character set
+#define MAX_WALPHA 0x24f
+// indexed by character - 0x80
+// 0=not alphabetic, 0xff=lower case, 0xfe=no case, 0xfd=use wchar_tolower
+// other=value to add to upper case to convert to lower case
+static unsigned char walpha_tab[MAX_WALPHA-0x7f] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 080
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 090
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xfe, 0, 0, 0, 0, 0, // 0a0
+ 0, 0, 0, 0, 0, 0xff, 0, 0, 0, 0, 0xfe, 0, 0, 0, 0, 0, // 0b0
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 0c0
+ 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 0xff, // 0d0
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 0e0
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 0f0
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 100
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 110
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 120
+ 0xfd, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 0xfe, 1, 0xff, 1, 0xff, 1, 0xff, 1, // 130
+ 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 0xfe, 1, 0xff, 1, 0xff, 1, 0xff, // 140
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 150
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 160
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 0xfd, 1, 0xff, 1, 0xff, 1, 0xff, 0xff, // 170
+ 0xff, 210, 1, 0xff, 1, 0xff, 206, 1, 0xff, 205, 205, 1, 0xff, 0xfe, 79, 202, // 180
+ 203, 1, 0xff, 205, 207, 0xff, 211, 209, 1, 0xff, 0xff, 0xfe, 211, 213, 0xff, 214, // 190
+ 1, 0xff, 1, 0xff, 1, 0xff, 218, 1, 0xff, 218, 0xfe, 0xfe, 1, 0xff, 218, 1, // 1a0
+ 0xff, 217, 217, 1, 0xff, 1, 0xff, 219, 1, 0xff, 0xfe, 0xfe, 1, 0xff, 0xfe, 0xff, // 1b0
+ 0xfe, 0xfe, 0xfe, 0xfe, 2, 0xff, 0xff, 2, 0xff, 0xff, 2, 0xff, 0xff, 1, 0xff, 1, // 1c0
+ 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 0xff, 1, 0xff, // 1d0
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 1e0
+ 0xfe, 2, 0xff, 0xff, 1, 0xff, 0xfd, 0xfd, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 1f0
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 200
+ 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 210
+ 0xfd, 0xfe, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, // 220
+ 1, 0xff, 1, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 1, 0xff, 0xfd, 0xfd, 0xfe, // 230
+ 0xfe, 1, 0xff, 0xfd, 69, 71, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff}; // 240
+
+static const short wchar_tolower[] = {
+ 0x130, 0x069,
+ 0x178, 0x0ff,
+ 0x1f6, 0x195,
+ 0x1f7, 0x1bf,
+ 0x220, 0x19e,
+ 0x23a, 0x2c65,
+ 0x23d, 0x19a,
+ 0x23e, 0x2c66,
+ 0x243, 0x180,
+ 0,0 };
+
+static const short wchar_toupper[] = {
+ 0x0b5, 0x39c,
+ 0x0df, 0x0df,
+ 0x0ff, 0x178,
+ 0x131, 0x049,
+ 0x17f, 0x053,
+ 0x180, 0x243,
+ 0x195, 0x1f6,
+ 0x19a, 0x23d,
+ 0x19e, 0x220,
+ 0x1bf, 0x1f7,
+ 0x1c6, 0x1c4,
+ 0x1c9, 0x1c7,
+ 0x1cc, 0x1ca,
+ 0x1dd, 0x18e,
+ 0x1f3, 0x1f1,
+ 0,0 };
-// additional Latin characters beyond the Latin1 character set
-#define MAX_WALPHA 0x233
-// indexed by character - 0x100
-// 0=not alphabetic, 0xff=lower case, other=value to add to upper case to convert to lower case
-static unsigned char walpha_tab[MAX_WALPHA-0xff] = {
- 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 100
- 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 110
- 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 120
- 0xff,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, 1,0xff, 1,0xff, 1,0xff, 1, // 130
- 0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, 1,0xff, 1,0xff, 1,0xff, // 140
- 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 150
- 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 160
- 1,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, // 170
- 0xff, 210, 1,0xff, 1,0xff, 206, 1,0xff, 205, 205, 1,0xff,0xff, 79, 202, // 180
- 203, 1,0xff, 205, 207,0xff, 211, 209, 1,0xff,0xff,0xff, 211, 213,0xff, 214, // 190
- 1,0xff, 1,0xff, 1,0xff, 218, 1,0xff, 218,0xff,0xff, 1,0xff, 218, 1, // 1a0
- 0xff, 217, 217, 1,0xff, 1,0xff, 219, 1,0xff,0xff,0xff, 1,0xff,0xff,0xff, // 1b0
- 0xff,0xff,0xff,0xff, 2, 1,0xff, 2, 1,0xff, 2, 1,0xff, 1,0xff, 1, // 1c0
- 0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff,0xff, 1,0xff, // 1d0
- 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 1e0
- 0xff, 2, 1,0xff, 1,0xff,0xff,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 1f0
- 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 200
- 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 210
- 0xff, 0, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, 1,0xff, // 220
- 1,0xff, 1,0xff }; // 230
+
+#ifdef NEED_WCHAR_FUNCTIONS
// use ctype.h functions for Latin1 (character < 0x100)
int iswalpha(int c)
{
- if(c < 0x100)
+ if(c < 0x80)
return(isalpha(c));
if((c > 0x3040) && (c <= 0xa700))
return(1); // japanese, chinese characters
if(c > MAX_WALPHA)
return(0);
- return(walpha_tab[c-0x100]);
+ return(walpha_tab[c-0x80]);
}
int iswdigit(int c)
{
- if(c < 0x100)
+ if(c < 0x80)
return(isdigit(c));
return(0);
}
@@ -247,44 +314,67 @@ int iswalnum(int c)
int towlower(int c)
{
int x;
- if(c < 0x100)
+ int ix;
+
+ if(c < 0x80)
return(tolower(c));
- if((c > MAX_WALPHA) || ((x = walpha_tab[c-0x100])==0xff))
- return(c); // already lower case
+
+ if((c > MAX_WALPHA) || ((x = walpha_tab[c-0x80]) >= 0xfe))
+ return(c);
+
+ if(x == 0xfd)
+ {
+ // special cases, lookup translation table
+ for(ix=0; wchar_tolower[ix] != 0; ix+=2)
+ {
+ if(wchar_tolower[ix] == c)
+ return(wchar_tolower[ix+1]);
+ }
+ }
return(c + x); // convert to lower case
}
int towupper(int c)
{
- // check whether the previous character code is the upper-case equivalent of this character
- if(tolower(c-1) == c)
- return(c-1); // yes, use it
+ int ix;
+ // check whether a previous character code is the upper-case equivalent of this character
+ if(towlower(c-32) == c)
+ return(c-32); // yes, use it
+ if(towlower(c-1) == c)
+ return(c-1);
+ for(ix=0; wchar_toupper[ix] != 0; ix+=2)
+ {
+ if(wchar_toupper[ix] == c)
+ return(wchar_toupper[ix+1]);
+ }
return(c); // no
}
int iswupper(int c)
{
int x;
- if(c < 0x100)
+ if(c < 0x80)
return(isupper(c));
- if(((c > MAX_WALPHA) || (x = walpha_tab[c-0x100])==0) || (x == 0xff))
+ if(((c > MAX_WALPHA) || (x = walpha_tab[c-0x80])==0) || (x == 0xff))
return(0);
return(1);
}
int iswlower(int c)
{
- if(c < 0x100)
+ if(c < 0x80)
return(islower(c));
- if((c > MAX_WALPHA) || (walpha_tab[c-0x100] != 0xff))
+ if((c > MAX_WALPHA) || (walpha_tab[c-0x80] != 0xff))
return(0);
return(1);
}
int iswspace(int c)
{
- if(c < 0x100)
+ if(c < 0x80)
return(isspace(c));
+ if(c == 0xa0)
+ return(1);
return(0);
}
@@ -336,19 +426,105 @@ float wcstod(const wchar_t *str, wchar_t **tailptr)
}
#endif
+
+// use internal data for iswalpha up to U+024F
+// iswalpha() on Windows is unreliable (U+AA, U+BA).
+int iswalpha2(int c)
+{
+ if(c < 0x80)
+ return(isalpha(c));
+ if((c > 0x3040) && (c <= 0xa700))
+ return(1); // japanese, chinese characters
+ if(c > MAX_WALPHA)
+ return(iswalpha(c));
+ return(walpha_tab[c-0x80]);
+}
+
+int iswlower2(int c)
+{
+ if(c < 0x80)
+ return(islower(c));
+ if(c > MAX_WALPHA)
+ return(iswlower(c));
+ if(walpha_tab[c-0x80] == 0xff)
+ return(1);
+ return(0);
+}
+
+int iswupper2(int c)
+{
+ int x;
+ if(c < 0x80)
+ return(isupper(c));
+ if(c > MAX_WALPHA)
+ return(iswupper(c));
+ if(((x = walpha_tab[c-0x80]) > 0) && (x < 0xfe))
+ return(1);
+ return(0);
+}
+
int towlower2(unsigned int c)
{
+ int x;
+ int ix;
+
// check for non-standard upper to lower case conversions
if(c == 'I')
{
- if(translator->translator_name == L('t','r'))
+ if(translator->langopts.dotless_i)
{
c = 0x131; // I -> ı
}
}
- return(towlower(c));
+
+ if(c < 0x80)
+ return(tolower(c));
+
+ if(c > MAX_WALPHA)
+ return(towlower(c));
+
+ if((x = walpha_tab[c-0x80]) >= 0xfe)
+ return(c); // this is not an upper case letter
+
+ if(x == 0xfd)
+ {
+ // special cases, lookup translation table
+ for(ix=0; wchar_tolower[ix] != 0; ix+=2)
+ {
+ if(wchar_tolower[ix] == (int)c)
+ return(wchar_tolower[ix+1]);
+ }
+ }
+ return(c + x); // convert to lower case
}
+int towupper2(unsigned int c)
+{
+ int ix;
+ if(c > MAX_WALPHA)
+ return(towupper(c));
+
+ // check whether a previous character code is the upper-case equivalent of this character
+ if(towlower2(c-32) == (int)c)
+ return(c-32); // yes, use it
+ if(towlower2(c-1) == (int)c)
+ return(c-1);
+ for(ix=0; wchar_toupper[ix] != 0; ix+=2)
+ {
+ if(wchar_toupper[ix] == (int)c)
+ return(wchar_toupper[ix+1]);
+ }
+ return(c); // no
+}
+
+static int IsRomanU(unsigned int c)
+{//================================
+ if((c=='I') || (c=='V') || (c=='X') || (c=='L'))
+ return(1);
+ return(0);
+}
+
+
static void GetC_unget(int c)
{//==========================
// This is only called with UTF8 input, not wchar input
@@ -438,10 +614,8 @@ static int GetC(void)
int cbuf[4];
int ix;
int n_bytes;
- unsigned char m;
static int ungot2 = 0;
static const unsigned char mask[4] = {0xff,0x1f,0x0f,0x07};
- static const unsigned char mask2[4] = {0,0x80,0x20,0x30};
if((c1 = ungot_char) != 0)
{
@@ -482,7 +656,6 @@ static int GetC(void)
if((ix = n_bytes) > 0)
{
c = c1 & mask[ix];
- m = mask2[ix];
while(ix > 0)
{
if((c2 = cbuf[ix] = GetC_get()) == 0)
@@ -501,7 +674,6 @@ static int GetC(void)
GetC_unget(c2);
break;
}
- m = 0x80;
c = (c << 6) + (c2 & 0x3f);
ix--;
}
@@ -530,8 +702,8 @@ static void UngetC(int c)
}
-static const char *WordToString2(unsigned int word)
-{//================================================
+const char *WordToString2(unsigned int word)
+{//============================================
// Convert a language mnemonic word into a string
int ix;
static char buf[5];
@@ -555,20 +727,20 @@ static const char *LookupSpecial(Translator *tr, const char *string, char* text_
char phonemes2[55];
char *string1 = (char *)string;
+ flags[0] = flags[1] = 0;
if(LookupDictList(tr,&string1,phonemes,flags,0,NULL))
{
- SetWordStress(tr, phonemes, &flags[0], -1, 0);
+ SetWordStress(tr, phonemes, flags, -1, 0);
DecodePhonemes(phonemes,phonemes2);
- sprintf(text_out,"[[%s]]",phonemes2);
- option_phoneme_input |= 2;
+ sprintf(text_out,"[\002%s]]",phonemes2);
return(text_out);
}
return(NULL);
}
-static const char *LookupCharName(Translator *tr, int c)
-{//=====================================================
+static const char *LookupCharName(Translator *tr, int c, int only)
+{//===============================================================
// Find the phoneme string (in ascii) to speak the name of character c
// Used for punctuation characters and symbols
@@ -589,20 +761,28 @@ static const char *LookupCharName(Translator *tr, int c)
ix = utf8_out(c,&single_letter[2]);
single_letter[2+ix]=0;
- string = &single_letter[1];
- if(LookupDictList(tr, &string, phonemes, flags, 0, NULL) == 0)
+ if(only)
{
- // try _* then *
string = &single_letter[2];
+ LookupDictList(tr, &string, phonemes, flags, 0, NULL);
+ }
+ else
+ {
+ string = &single_letter[1];
if(LookupDictList(tr, &string, phonemes, flags, 0, NULL) == 0)
{
- // now try the rules
- single_letter[1] = ' ';
- TranslateRules(tr, &single_letter[2], phonemes, sizeof(phonemes), NULL,0,NULL);
+ // try _* then *
+ string = &single_letter[2];
+ if(LookupDictList(tr, &string, phonemes, flags, 0, NULL) == 0)
+ {
+ // now try the rules
+ single_letter[1] = ' ';
+ TranslateRules(tr, &single_letter[2], phonemes, sizeof(phonemes), NULL,0,NULL);
+ }
}
}
- if((phonemes[0] == 0) && (tr->translator_name != L('e','n')))
+ if((only==0) && ((phonemes[0] == 0) || (phonemes[0] == phonSWITCH)) && (tr->translator_name != L('e','n')))
{
// not found, try English
SetTranslator2("en");
@@ -627,23 +807,22 @@ static const char *LookupCharName(Translator *tr, int c)
{
if(lang_name)
{
- SetWordStress(translator2, phonemes, &flags[0], -1, 0);
+ SetWordStress(translator2, phonemes, flags, -1, 0);
DecodePhonemes(phonemes,phonemes2);
- sprintf(buf,"[[_^_%s %s _^_%s]]","en",phonemes2,WordToString2(tr->translator_name));
+ sprintf(buf,"[\002_^_%s %s _^_%s]]","en",phonemes2,WordToString2(tr->translator_name));
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
}
else
{
- SetWordStress(tr, phonemes, &flags[0], -1, 0);
+ SetWordStress(tr, phonemes, flags, -1, 0);
DecodePhonemes(phonemes,phonemes2);
- sprintf(buf,"[[%s]] ",phonemes2);
+ sprintf(buf,"[\002%s]] ",phonemes2);
}
- option_phoneme_input |= 2;
}
else
+ if(only == 0)
{
- strcpy(buf,"[[(X1)(X1)(X1)]]");
- option_phoneme_input |= 2;
+ strcpy(buf,"[\002(X1)(X1)(X1)]]");
}
return(buf);
@@ -671,6 +850,7 @@ static int LoadSoundFile(const char *fname, int index)
char *p;
int *ip;
int length;
+ char fname_temp[100];
char fname2[sizeof(path_home)+13+40];
if(fname == NULL)
@@ -690,15 +870,15 @@ static int LoadSoundFile(const char *fname, int index)
}
f = NULL;
+
+#ifndef _WIN32
#ifdef PLATFORM_POSIX
if((f = fopen(fname,"rb")) != NULL)
{
int ix;
int fd_temp;
- const char *resample;
int header[3];
char command[sizeof(fname2)+sizeof(fname2)+40];
- char fname_temp[100];
fseek(f,20,SEEK_SET);
for(ix=0; ix<3; ix++)
@@ -710,17 +890,11 @@ static int LoadSoundFile(const char *fname, int index)
fclose(f);
f = NULL;
- if(header[2] == samplerate)
- resample = "";
- else
- resample = "polyphase";
-
strcpy(fname_temp,"/tmp/espeakXXXXXX");
if((fd_temp = mkstemp(fname_temp)) >= 0)
{
close(fd_temp);
-// sprintf(fname_temp,"%s.wav",tmpnam(NULL));
- sprintf(command,"sox \"%s\" -r %d -w -s -c1 %s %s\n", fname, samplerate, fname_temp, resample);
+ sprintf(command,"sox \"%s\" -r %d -c1 -t wav %s\n", fname, samplerate, fname_temp);
if(system(command) == 0)
{
fname = fname_temp;
@@ -729,13 +903,14 @@ static int LoadSoundFile(const char *fname, int index)
}
}
#endif
+#endif
if(f == NULL)
{
f = fopen(fname,"rb");
if(f == NULL)
{
- fprintf(stderr,"Can't read temp file: %s\n",fname);
+// fprintf(stderr,"Can't read temp file: %s\n",fname);
return(3);
}
}
@@ -747,9 +922,9 @@ static int LoadSoundFile(const char *fname, int index)
fclose(f);
return(4);
}
- fread(p,length,1,f);
+ length = fread(p,1,length,f);
fclose(f);
-#if 0
+#ifndef _WIN32
remove(fname_temp);
#endif
@@ -810,57 +985,92 @@ static int LoadSoundFile2(const char *fname)
-static int AnnouncePunctuation(Translator *tr, int c1, int c2, char *buf, int bufix)
-{//=================================================================================
+static int AnnouncePunctuation(Translator *tr, int c1, int *c2_ptr, char *output, int *bufix, int end_clause)
+{//=============================================================================================================
// announce punctuation names
// c1: the punctuation character
// c2: the following character
int punct_count;
- const char *punctname;
- int found = 0;
+ const char *punctname = NULL;
int soundicon;
- char *p;
+ int attributes;
+ int short_pause;
+ int c2;
+ int len;
+ int bufix1;
+ char buf[200];
+ char buf2[80];
+ char ph_buf[30];
+
+ c2 = *c2_ptr;
+ buf[0] = 0;
if((soundicon = LookupSoundicon(c1)) >= 0)
{
// add an embedded command to play the soundicon
- sprintf(&buf[bufix],"\001%dI ",soundicon);
+ sprintf(buf,"\001%dI ",soundicon);
UngetC(c2);
- found = 1;
}
else
- if((punctname = LookupCharName(tr, c1)) != NULL)
{
- found = 1;
- if(bufix==0)
+ if((c1 == '.') && (end_clause) && (c2 != '.'))
+ {
+ if(LookupSpecial(tr, "_.p", ph_buf))
+ {
+ punctname = ph_buf; // use word for 'period' instead of 'dot'
+ }
+ }
+ if(punctname == NULL)
+ {
+ punctname = LookupCharName(tr, c1, 0);
+ }
+
+ if(punctname == NULL)
+ return(-1);
+
+ if((*bufix==0) || (end_clause ==0) || (tr->langopts.param[LOPT_ANNOUNCE_PUNCT] & 2))
{
punct_count=1;
- while(c2 == c1)
+ while((c2 == c1) && (c1 != '<')) // don't eat extra '<', it can miss XML tags
{
punct_count++;
c2 = GetC();
}
- UngetC(c2);
+ *c2_ptr = c2;
+ if(end_clause)
+ {
+ UngetC(c2);
+ }
- p = &buf[bufix];
if(punct_count==1)
{
- sprintf(p,"%s %s %s",tone_punct_on,punctname,tone_punct_off);
+// sprintf(buf,"%s %s %s",tone_punct_on,punctname,tone_punct_off);
+ sprintf(buf," %s",punctname); // we need the space before punctname, to ensure it doesn't merge with the previous word (eg. "2.-a")
}
else
if(punct_count < 4)
{
- sprintf(p,"\001+10S%s",tone_punct_on);
+ buf[0] = 0;
+ if(embedded_value[EMBED_S] < 300)
+ sprintf(buf,"\001+10S"); // Speak punctuation name faster, unless we are already speaking fast. It would upset Sonic SpeedUp
+
while(punct_count-- > 0)
- sprintf(buf,"%s %s",buf,punctname);
- sprintf(p,"%s %s\001-10S",buf,tone_punct_off);
+ {
+ sprintf(buf2," %s",punctname);
+ strcat(buf, buf2);
+ }
+
+ if(embedded_value[EMBED_S] < 300)
+ {
+ sprintf(buf2," \001-10S");
+ strcat(buf, buf2);
+ }
}
else
{
- sprintf(p,"%s %s %d %s %s",
- tone_punct_on,punctname,punct_count,punctname,tone_punct_off);
- return(CLAUSE_COMMA);
+ sprintf(buf," %s %d %s",
+ punctname,punct_count,punctname);
}
}
else
@@ -873,24 +1083,39 @@ static int AnnouncePunctuation(Translator *tr, int c1, int c2, char *buf, int bu
ssml_ignore_l_angle = c1; // this was &lt; which was converted to <, don't pick it up again as <
}
ungot_char2 = c1;
- buf[bufix] = ' ';
- buf[bufix+1] = 0;
+ buf[0] = ' ';
+ buf[1] = 0;
}
}
- if(found == 0)
+ bufix1 = *bufix;
+ len = strlen(buf);
+ strcpy(&output[*bufix],buf);
+ *bufix += len;
+
+ if(end_clause==0)
return(-1);
if(c1 == '-')
return(CLAUSE_NONE); // no pause
- if(bufix > 0)
- return(CLAUSE_SHORTCOMMA);
- if((strchr_w(punct_close,c1) != NULL) && !iswalnum(c2))
- return(CLAUSE_SHORTFALL+4);
- if(iswspace(c2) && strchr_w(punct_stop,c1)!=NULL)
- return(punct_attributes[lookupwchar(punct_chars,c1)]);
-
- return(CLAUSE_SHORTCOMMA);
+
+ attributes = punct_attributes[lookupwchar(punct_chars,c1)];
+
+ short_pause = CLAUSE_SHORTFALL;
+ if((attributes & CLAUSE_BITS_INTONATION) == 0x1000)
+ short_pause = CLAUSE_SHORTCOMMA;
+
+ if((bufix1 > 0) && !(tr->langopts.param[LOPT_ANNOUNCE_PUNCT] & 2))
+ {
+ if((attributes & ~0x8000) == CLAUSE_SEMICOLON)
+ return(CLAUSE_SHORTFALL);
+ return(short_pause);
+ }
+
+ if(attributes & CLAUSE_BIT_SENTENCE)
+ return(attributes);
+
+ return(short_pause);
} // end of AnnouncePunctuation
#define SSML_SPEAK 1
@@ -908,10 +1133,11 @@ static int AnnouncePunctuation(Translator *tr, int c1, int c2, char *buf, int bu
#define SSML_BREAK 13
#define SSML_IGNORE_TEXT 14
#define HTML_BREAK 15
-#define SSML_CLOSE 0x10 // for a closing tag, OR this with the tag type
+#define HTML_NOSPACE 16 // don't insert a space for this element, so it doesn't break a word
+#define SSML_CLOSE 0x20 // for a closing tag, OR this with the tag type
// these tags have no effect if they are self-closing, eg. <voice />
-static char ignore_if_self_closing[] = {0,1,1,1,1,0,0,0,0,1,1,0,1,0,1,0,0};
+static char ignore_if_self_closing[] = {0,1,1,1,1,0,0,0,0,1,1,0,1,0,1,0,0,0,0};
static MNEM_TAB ssmltags[] = {
@@ -932,6 +1158,7 @@ static MNEM_TAB ssmltags[] = {
{"br", HTML_BREAK},
{"li", HTML_BREAK},
+ {"dd", HTML_BREAK},
{"img", HTML_BREAK},
{"td", HTML_BREAK},
{"h1", SSML_PARAGRAPH},
@@ -941,29 +1168,37 @@ static MNEM_TAB ssmltags[] = {
{"hr", SSML_PARAGRAPH},
{"script", SSML_IGNORE_TEXT},
{"style", SSML_IGNORE_TEXT},
+ {"font", HTML_NOSPACE},
+ {"b", HTML_NOSPACE},
+ {"i", HTML_NOSPACE},
+ {"strong", HTML_NOSPACE},
+ {"em", HTML_NOSPACE},
+ {"code", HTML_NOSPACE},
{NULL,0}};
-static const char *VoiceFromStack()
+static const char *VoiceFromStack(void)
{//================================
// Use the voice properties from the SSML stack to choose a voice, and switch
// to that voice if it's not the current voice
int ix;
+ const char *p;
SSML_STACK *sp;
const char *v_id;
int voice_name_specified;
int voice_found;
espeak_VOICE voice_select;
- char voice_name[40];
+ static char voice_name[40];
char language[40];
+ char buf[80];
strcpy(voice_name,ssml_stack[0].voice_name);
strcpy(language,ssml_stack[0].language);
voice_select.age = ssml_stack[0].voice_age;
voice_select.gender = ssml_stack[0].voice_gender;
- voice_select.variant = ssml_stack[0].voice_variant;
+ voice_select.variant = ssml_stack[0].voice_variant_number;
voice_select.identifier = NULL;
for(ix=0; ix<n_ssml_stack; ix++)
@@ -983,15 +1218,32 @@ static const char *VoiceFromStack()
if(sp->language[0] != 0)
{
strcpy(language, sp->language);
+
+ // is this language provided by the base voice?
+ p = base_voice.languages;
+ while(*p++ != 0)
+ {
+ if(strcmp(p, language) == 0)
+ {
+ // yes, change the language to the main language of the base voice
+ strcpy(language, &base_voice.languages[1]);
+ break;
+ }
+ p += (strlen(p) + 1);
+ }
+
if(voice_name_specified == 0)
voice_name[0] = 0; // forget a previous voice name if a language is specified
}
if(sp->voice_gender != 0)
+ {
voice_select.gender = sp->voice_gender;
+ }
+
if(sp->voice_age != 0)
voice_select.age = sp->voice_age;
- if(sp->voice_variant != 0)
- voice_select.variant = sp->voice_variant;
+ if(sp->voice_variant_number != 0)
+ voice_select.variant = sp->voice_variant_number;
}
voice_select.name = voice_name;
@@ -999,6 +1251,14 @@ static const char *VoiceFromStack()
v_id = SelectVoice(&voice_select, &voice_found);
if(v_id == NULL)
return("default");
+
+ if((strchr(v_id, '+') == NULL) && ((voice_select.gender == 0) || (voice_select.gender == base_voice.gender)) && (base_voice_variant_name[0] != 0))
+ {
+ // a voice variant has not been selected, use the original voice variant
+ sprintf(buf, "%s+%s", v_id, base_voice_variant_name);
+ strncpy0(voice_name, buf, sizeof(voice_name));
+ return(voice_name);
+ }
return(v_id);
} // end of VoiceFromStack
@@ -1012,7 +1272,7 @@ static void ProcessParamStack(char *outbuf, int *outix)
int value;
char buf[20];
int new_parameters[N_SPEECH_PARAM];
- static char cmd_letter[N_SPEECH_PARAM] = {0, 'S','A','P','R', 0, 0, 0, 0, 0, 0, 0, 'F'}; // embedded command letters
+ static char cmd_letter[N_SPEECH_PARAM] = {0, 'S','A','P','R', 0, 'C', 0, 0, 0, 0, 0, 'F'}; // embedded command letters
for(param=0; param<N_SPEECH_PARAM; param++)
@@ -1054,7 +1314,7 @@ static void ProcessParamStack(char *outbuf, int *outix)
speech_parameters[param] = new_parameters[param];
strcpy(&outbuf[*outix],buf);
- (*outix) += strlen(buf);
+ *outix += strlen(buf);
}
}
} // end of ProcessParamStack
@@ -1126,7 +1386,7 @@ static wchar_t *GetSsmlAttribute(wchar_t *pw, const char *name)
while(iswspace(*pw)) pw++;
if(*pw == '=') pw++;
while(iswspace(*pw)) pw++;
- if(*pw == '"')
+ if((*pw == '"') || (*pw == '\'')) // allow single-quotes ?
return(pw+1);
else
return(empty);
@@ -1148,7 +1408,7 @@ static int attrcmp(const wchar_t *string1, const char *string2)
for(ix=0; (string1[ix] == string2[ix]) && (string1[ix] != 0); ix++)
{
}
- if((string1[ix]=='"') && (string2[ix]==0))
+ if(((string1[ix]=='"') || (string1[ix]=='\'')) && (string2[ix]==0))
return(0);
return(1);
}
@@ -1171,10 +1431,10 @@ static int attrnumber(const wchar_t *pw, int default_value, int type)
{//==================================================================
int value = 0;
- if((pw == NULL) || !isdigit(*pw))
+ if((pw == NULL) || !IsDigit09(*pw))
return(default_value);
- while(isdigit(*pw))
+ while(IsDigit09(*pw))
{
value = value*10 + *pw++ - '0';
}
@@ -1217,7 +1477,7 @@ static int attr_prosody_value(int param_type, const wchar_t *pw, int *value_out)
{//=============================================================================
int sign = 0;
wchar_t *tail;
- float value;
+ double value;
while(iswspace(*pw)) pw++;
if(*pw == '+')
@@ -1230,7 +1490,7 @@ static int attr_prosody_value(int param_type, const wchar_t *pw, int *value_out)
pw++;
sign = -1;
}
- value = (float)wcstod(pw,&tail);
+ value = (double)wcstod(pw,&tail);
if(tail == pw)
{
// failed to find a number, return 100%
@@ -1248,16 +1508,23 @@ static int attr_prosody_value(int param_type, const wchar_t *pw, int *value_out)
if((tail[0]=='s') && (tail[1]=='t'))
{
+#ifdef PLATFORM_RISCOS
+ *value_out = 100;
+#else
double x;
// convert from semitones to a frequency percentage
- x = pow(double(2.0),double((value*sign)/12)) * 100;
+ x = pow((double)2.0,(double)((value*sign)/12)) * 100;
*value_out = (int)x;
+#endif
return(2); // percentage
}
if(param_type == espeakRATE)
{
- *value_out = (int)(value * 100);
+ if(sign == 0)
+ *value_out = (int)(value * 100);
+ else
+ *value_out = 100 + (int)(sign * value * 100);
return(2); // percentage
}
@@ -1266,7 +1533,7 @@ static int attr_prosody_value(int param_type, const wchar_t *pw, int *value_out)
} // end of attr_prosody_value
-int AddNameData(const char *name, int wide)
+static int AddNameData(const char *name, int wide)
{//========================================
// Add the name to the namedata and return its position
// (Used by the Windows SAPI wrapper)
@@ -1287,11 +1554,12 @@ int AddNameData(const char *name, int wide)
if(namedata_ix+len >= n_namedata)
{
// allocate more space for marker names
- if((vp = realloc(namedata, namedata_ix+len + 300)) == NULL)
+ if((vp = realloc(namedata, namedata_ix+len + 1000)) == NULL)
return(-1); // failed to allocate, original data is unchanged but ignore this new name
+// !!! Bug?? If the allocated data shifts position, then pointers given to user application will be invalid
namedata = (char *)vp;
- n_namedata = namedata_ix+len + 300;
+ n_namedata = namedata_ix+len + 1000;
}
memcpy(&namedata[ix = namedata_ix],name,len);
namedata_ix += len;
@@ -1299,8 +1567,8 @@ int AddNameData(const char *name, int wide)
} // end of AddNameData
-void SetVoiceStack(espeak_VOICE *v)
-{//================================
+void SetVoiceStack(espeak_VOICE *v, const char *variant_name)
+{//==========================================================
SSML_STACK *sp;
sp = &ssml_stack[0];
@@ -1312,10 +1580,15 @@ void SetVoiceStack(espeak_VOICE *v)
if(v->languages != NULL)
strcpy(sp->language,v->languages);
if(v->name != NULL)
- strcpy(sp->voice_name,v->name);
- sp->voice_variant = v->variant;
+ strncpy0(sp->voice_name, v->name, sizeof(sp->voice_name));
+ sp->voice_variant_number = v->variant;
sp->voice_age = v->age;
sp->voice_gender = v->gender;
+
+ if(memcmp(variant_name, "!v", 2) == 0)
+ variant_name += 3;// strip variant directory name, !v plus PATHSEP
+ strncpy0(base_voice_variant_name, variant_name, sizeof(base_voice_variant_name));
+ memcpy(&base_voice, &current_voice_selected, sizeof(base_voice));
}
@@ -1332,6 +1605,7 @@ static int GetVoiceAttributes(wchar_t *pw, int tag_type)
wchar_t *name;
wchar_t *age;
wchar_t *variant;
+ int value;
const char *new_voice_id;
SSML_STACK *ssml_sp;
@@ -1377,7 +1651,9 @@ static int GetVoiceAttributes(wchar_t *pw, int tag_type)
attrcopy_utf8(ssml_sp->language,lang,sizeof(ssml_sp->language));
attrcopy_utf8(ssml_sp->voice_name,name,sizeof(ssml_sp->voice_name));
- ssml_sp->voice_variant = attrnumber(variant,1,0)-1;
+ if((value = attrnumber(variant,1,0)) > 0)
+ value--; // variant='0' and variant='1' the same
+ ssml_sp->voice_variant_number = value;
ssml_sp->voice_age = attrnumber(age,0,0);
ssml_sp->voice_gender = attrlookup(gender,mnem_gender);
ssml_sp->tag_type = tag_type;
@@ -1415,8 +1691,8 @@ static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp)
{"x-slow",60},
{"slow",80},
{"medium",100},
- {"fast",120},
- {"x-fast",150},
+ {"fast",125},
+ {"x-fast",160},
{NULL, -1}};
static const MNEM_TAB mnem_pitch[] = {
@@ -1467,6 +1743,31 @@ static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp)
} // end of SetProsodyParemeter
+static int ReplaceKeyName(char *outbuf, int index, int *outix)
+{//===========================================================
+// Replace some key-names by single characters, so they can be pronounced in different languages
+ static MNEM_TAB keynames[] = {
+ {"space ",0xe020},
+ {"tab ", 0xe009},
+ {"underscore ", 0xe05f},
+ {"double-quote ", '"'},
+ {NULL, 0}};
+
+ int ix;
+ int letter;
+ char *p;
+
+ p = &outbuf[index];
+
+ if((letter = LookupMnem(keynames, p)) != 0)
+ {
+ ix = utf8_out(letter, p);
+ *outix = index + ix;
+ return(letter);
+ }
+ return(0);
+}
+
static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, int self_closing)
{//==================================================================================================
@@ -1532,6 +1833,7 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
{"reduced",2},
{"moderate",3},
{"strong",4},
+ {"x-strong",5},
{NULL,-1}};
static const char *prosody_attr[5] = {
@@ -1549,16 +1851,26 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
if(tag_name[0] == '/')
{
- tag_type = LookupMnem(ssmltags,&tag_name[1]) + SSML_CLOSE; // closing tag
+ // closing tag
+ if((tag_type = LookupMnem(ssmltags,&tag_name[1])) != HTML_NOSPACE)
+ {
+ outbuf[(*outix)++] = ' ';
+ }
+ tag_type += SSML_CLOSE;
}
else
{
- tag_type = LookupMnem(ssmltags,tag_name);
+ if((tag_type = LookupMnem(ssmltags,tag_name)) != HTML_NOSPACE)
+ {
+ // separate SSML tags from the previous word (but not HMTL tags such as <b> <font> which can occur inside a word)
+ outbuf[(*outix)++] = ' ';
+ }
if(self_closing && ignore_if_self_closing[tag_type])
return(0);
}
+
voice_change_flag = 0;
terminator = CLAUSE_NONE;
ssml_sp = &ssml_stack[n_ssml_stack-1];
@@ -1610,14 +1922,16 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
if(translator->langopts.tone_language == 1)
{
- static unsigned char emphasis_to_pitch_range[] = {50,50,40,70,90,90};
- static unsigned char emphasis_to_volume[] = {100,100,70,110,140,140};
+ static unsigned char emphasis_to_pitch_range[] = {50,50,40,70,90,100};
+ static unsigned char emphasis_to_volume[] = {100,100,70,110,135,150};
// tone language (eg.Chinese) do emphasis by increasing the pitch range.
sp->parameter[espeakRANGE] = emphasis_to_pitch_range[value];
sp->parameter[espeakVOLUME] = emphasis_to_volume[value];
}
else
{
+ static unsigned char emphasis_to_volume2[] = {100,100,75,100,120,150};
+ sp->parameter[espeakVOLUME] = emphasis_to_volume2[value];
sp->parameter[espeakEMPHASIS] = value;
}
ProcessParamStack(outbuf, outix);
@@ -1650,12 +1964,19 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
sprintf(buf,"%c%dY",CTRL_EMBEDDED,value);
strcpy(&outbuf[*outix],buf);
- (*outix) += strlen(buf);
+ *outix += strlen(buf);
+ sayas_start = *outix;
sayas_mode = value; // punctuation doesn't end clause during SAY-AS
break;
case SSML_SAYAS + SSML_CLOSE:
+ if(sayas_mode == SAYAS_KEY)
+ {
+ outbuf[*outix] = 0;
+ ReplaceKeyName(outbuf, sayas_start, outix);
+ }
+
outbuf[(*outix)++] = CTRL_EMBEDDED;
outbuf[(*outix)++] = 'Y';
sayas_mode = 0;
@@ -1666,7 +1987,7 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
{
// use the alias rather than the text
ignore_text = 1;
- (*outix) += attrcopy_utf8(&outbuf[*outix],attr1,n_outbuf-*outix);
+ *outix += attrcopy_utf8(&outbuf[*outix],attr1,n_outbuf-*outix);
}
break;
@@ -1697,7 +2018,7 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
{
sprintf(buf,"%c%dM",CTRL_EMBEDDED,index);
strcpy(&outbuf[*outix],buf);
- (*outix) += strlen(buf);
+ *outix += strlen(buf);
}
}
break;
@@ -1725,7 +2046,7 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
{
sprintf(buf,"%c%dI",CTRL_EMBEDDED,index);
strcpy(&outbuf[*outix],buf);
- (*outix) += strlen(buf);
+ *outix += strlen(buf);
sp->parameter[espeakSILENCE] = 1;
}
}
@@ -1738,7 +2059,7 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
{
sprintf(buf,"%c%dU",CTRL_EMBEDDED,index);
strcpy(&outbuf[*outix],buf);
- (*outix) += strlen(buf);
+ *outix += strlen(buf);
sp->parameter[espeakSILENCE] = 1;
}
}
@@ -1748,10 +2069,13 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
if(self_closing)
PopParamStack(tag_type, outbuf, outix);
+ else
+ audio_text = 1;
return(CLAUSE_NONE);
case SSML_AUDIO + SSML_CLOSE:
PopParamStack(tag_type, outbuf, outix);
+ audio_text = 0;
return(CLAUSE_NONE);
case SSML_BREAK:
@@ -1766,14 +2090,20 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
{
// adjust prepause on the following word
sprintf(&outbuf[*outix],"%c%dB",CTRL_EMBEDDED,value);
- (*outix) += 3;
+ *outix += 3;
terminator = 0;
}
value = break_value[value];
}
if((attr2 = GetSsmlAttribute(px,"time")) != NULL)
{
- value = (attrnumber(attr2,0,1) * 25) / speed.speed_factor1; // compensate for speaking speed to keep constant pause length
+ value2 = attrnumber(attr2,0,1); // pause in mS
+
+ // compensate for speaking speed to keep constant pause length, see function PauseLength()
+ // 'value' here is x 10mS
+ value = (value2 * 256) / (speed.clause_pause_factor * 10);
+ if(value < 200)
+ value = (value2 * 256) / (speed.pause_factor * 10);
if(terminator == 0)
terminator = CLAUSE_NONE;
@@ -1781,7 +2111,13 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
if(terminator)
{
if(value > 0xfff)
- value = 0xfff;
+ {
+ // scale down the value and set a scaling indicator bit
+ value = value / 32;
+ if(value > 0xfff)
+ value = 0xfff;
+ terminator |= CLAUSE_PAUSE_LONG;
+ }
return(terminator + value);
}
break;
@@ -1873,9 +2209,18 @@ terminator=0; // ?? Sentence intonation, but no pause ??
} // end of ProcessSsmlTag
+static void RemoveChar(char *p)
+{//=======================
+// Replace a UTF-8 character by spaces
+ int c;
+
+ memset(p, ' ', utf8_in(&c, p));
+} // end of RemoveChar
+
+
static MNEM_TAB xml_char_mnemonics[] = {
{"gt",'>'},
- {"lt",'<'},
+ {"lt", 0xe000 + '<'}, // private usage area, to avoid confusion with XML tag
{"amp", '&'},
{"quot", '"'},
{"nbsp", ' '},
@@ -1883,8 +2228,8 @@ static MNEM_TAB xml_char_mnemonics[] = {
{NULL,-1}};
-int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix_top, int n_buf, int *tone_type)
-{//=============================================================================================================
+int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix_top, int n_buf, int *tone_type, char *voice_change)
+{//=================================================================================================================================
/* Find the end of the current clause.
Write the clause into buf
@@ -1899,6 +2244,8 @@ int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix
int c1=' '; // current character
int c2; // next character
int cprev=' '; // previous character
+ int cprev2=' ';
+ int c_next;
int parag;
int ix = 0;
int j;
@@ -1911,9 +2258,12 @@ int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix
int found;
int any_alnum = 0;
int self_closing;
- int punct_data;
+ int punct_data = 0;
+ int is_end_clause;
+ int announced_punctuation = 0;
int stressed_word = 0;
- const char *p;
+ int end_clause_after_tag = 0;
+ int end_clause_index = 0;
wchar_t xml_buf[N_XML_BUF+1];
#define N_XML_BUF2 20
@@ -1927,10 +2277,12 @@ int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix
clear_skipping_text = 0;
}
+ tr->phonemes_repeat_count = 0;
tr->clause_upper_count = 0;
tr->clause_lower_count = 0;
end_of_input = 0;
*tone_type = 0;
+ *voice_change = 0;
f_input = f_in; // for GetC etc
@@ -1960,7 +2312,7 @@ f_input = f_in; // for GetC etc
return(CLAUSE_EOF);
}
- if((skip_characters > 0) && (count_characters > skip_characters))
+ if((skip_characters > 0) && (count_characters >= skip_characters))
{
// reached the specified start position
// don't break a word
@@ -1971,6 +2323,7 @@ f_input = f_in; // for GetC etc
}
}
+ cprev2 = cprev;
cprev = c1;
c1 = c2;
@@ -2064,8 +2417,19 @@ f_input = f_in; // for GetC etc
c2 = ' ';
}
else
- if((c2 == '/') || iswalpha(c2))
+ if((c2 == '/') || iswalpha2(c2))
{
+ // check for space in the output buffer for embedded commands produced by the SSML tag
+ if(ix > (n_buf - 20))
+ {
+ // Perhaps not enough room, end the clause before the SSML tag
+ UngetC(c2);
+ ungot_char2 = c1;
+ buf[ix] = ' ';
+ buf[ix+1] = 0;
+ return(CLAUSE_NONE);
+ }
+
// SSML Tag
n_xml_buf = 0;
c1 = c2;
@@ -2077,8 +2441,6 @@ f_input = f_in; // for GetC etc
xml_buf[n_xml_buf] = 0;
c2 = ' ';
- buf[ix++] = ' ';
-
self_closing = 0;
if(xml_buf[n_xml_buf-1] == '/')
{
@@ -2087,25 +2449,24 @@ f_input = f_in; // for GetC etc
self_closing = 1;
}
- terminator = ProcessSsmlTag(xml_buf,buf,ix,n_buf,self_closing);
+ terminator = ProcessSsmlTag(xml_buf,buf,&ix,n_buf,self_closing);
if(terminator != 0)
{
+ if(end_clause_after_tag)
+ ix = end_clause_index;
+
buf[ix] = ' ';
buf[ix++] = 0;
if(terminator & CLAUSE_BIT_VOICE)
{
- // a change in voice, write the new voice name to the end of the buf
- p = current_voice_id;
- while((*p != 0) && (ix < (n_buf-1)))
- {
- buf[ix++] = *p++;
- }
- buf[ix++] = 0;
+ strcpy(voice_change, current_voice_id);
}
return(terminator);
}
+ c1 = ' ';
+ c2 = GetC();
continue;
}
}
@@ -2180,6 +2541,16 @@ f_input = f_in; // for GetC etc
linelength++;
+ if((j = lookupwchar2(tr->chars_ignore,c1)) != 0)
+ {
+ if(j == 1)
+ {
+ // ignore this character (eg. zero-width-non-joiner U+200C)
+ continue;
+ }
+ c1 = j; // replace the character
+ }
+
if(iswalnum(c1))
any_alnum = 1;
else
@@ -2192,6 +2563,9 @@ f_input = f_in; // for GetC etc
c2 = ' ';
}
+ if(c1 == 0xf0b)
+ c1 = ' '; // Tibet inter-syllabic mark, ?? replace by space ??
+
if(iswspace(c1))
{
char *p_word;
@@ -2215,18 +2589,27 @@ f_input = f_in; // for GetC etc
}
}
}
+
+ if(c1 == 0xd4d)
+ {
+ // Malayalam virama, check if next character is Zero-width-joiner
+ if(c2 == 0x200d)
+ {
+ c1 = 0xd4e; // use this unofficial code for chillu-virama
+ }
+ }
}
- if(iswupper(c1))
+ if(iswupper2(c1))
{
tr->clause_upper_count++;
- if((option_capitals == 2) && (sayas_mode == 0) && !iswupper(cprev))
+ if((option_capitals == 2) && (sayas_mode == 0) && !iswupper2(cprev))
{
char text_buf[40];
char text_buf2[30];
if(LookupSpecial(tr, "_cap", text_buf2) != NULL)
{
- sprintf(text_buf,"%s%s%s",tone_punct_on,text_buf2,tone_punct_off);
+ sprintf(text_buf,"%s",text_buf2);
j = strlen(text_buf);
if((ix + j) < n_buf)
{
@@ -2237,7 +2620,7 @@ f_input = f_in; // for GetC etc
}
}
else
- if(iswalpha(c1))
+ if(iswalpha2(c1))
tr->clause_lower_count++;
if(option_phoneme_input)
@@ -2268,6 +2651,10 @@ f_input = f_in; // for GetC etc
// 2nd newline, assume paragraph
UngetC(c2);
+ if(end_clause_after_tag)
+ {
+ RemoveChar(&buf[end_clause_index]); // delete clause-end punctiation
+ }
buf[ix] = ' ';
buf[ix+1] = 0;
if(parag > 3)
@@ -2288,88 +2675,231 @@ if(option_ssml) parag=1;
linelength = 0;
}
- if(option_punctuation && (phoneme_mode==0) && (sayas_mode==0) && iswpunct(c1))
+ announced_punctuation = 0;
+
+ if((phoneme_mode==0) && (sayas_mode==0))
{
- // option is set to explicitly speak punctuation characters
- // if a list of allowed punctuation has been set up, check whether the character is in it
- if((option_punctuation == 1) || (wcschr(option_punctlist,c1) != NULL))
+ is_end_clause = 0;
+
+ if(end_clause_after_tag)
{
- if((terminator = AnnouncePunctuation(tr, c1, c2, buf, ix)) >= 0)
- return(terminator);
+ // Because of an xml tag, we are waiting for the
+ // next non-blank character to decide whether to end the clause
+ // i.e. is dot followed by an upper-case letter?
+
+ if(!iswspace(c1))
+ {
+ if(!IsAlpha(c1) || !iswlower2(c1))
+// if(iswdigit(c1) || (IsAlpha(c1) && !iswlower2(c1)))
+ {
+ UngetC(c2);
+ ungot_char2 = c1;
+ buf[end_clause_index] = ' '; // delete the end-clause punctuation
+ buf[end_clause_index+1] = 0;
+ return(end_clause_after_tag);
+ }
+ end_clause_after_tag = 0;
+ }
}
- }
- if((phoneme_mode==0) && (sayas_mode==0) && ((punct = lookupwchar(punct_chars,c1)) != 0))
- {
- punct_data = punct_attributes[punct];
+ if((c1 == '.') && (c2 == '.'))
+ {
+ while((c_next = GetC()) == '.')
+ {
+ // 3 or more dots, replace by elipsis
+ c1 = 0x2026;
+ c2 = ' ';
+ }
+ if(c1 == 0x2026)
+ c2 = c_next;
+ else
+ UngetC(c_next);
+ }
- if(punct_data & PUNCT_IN_WORD)
+ punct_data = 0;
+ if((punct = lookupwchar(punct_chars,c1)) != 0)
{
- // Armenian punctuation inside a word
- stressed_word = 1;
- *tone_type = punct_data >> 12 & 0xf; // override the end-of-sentence type
- continue;
+ punct_data = punct_attributes[punct];
+
+ if(punct_data & PUNCT_IN_WORD)
+ {
+ // Armenian punctuation inside a word
+ stressed_word = 1;
+ *tone_type = punct_data >> 12 & 0xf; // override the end-of-sentence type
+ continue;
+ }
+
+ if((iswspace(c2) || (punct_data & 0x8000) || IsBracket(c2) || (c2=='?') || Eof() || (c2 == ctrl_embedded))) // don't check for '-' because it prevents recognizing ':-)'
+// if((iswspace(c2) || (punct_data & 0x8000) || IsBracket(c2) || (c2=='?') || (c2=='-') || Eof()))
+ {
+ // note: (c2='?') is for when a smart-quote has been replaced by '?'
+ is_end_clause = 1;
+ }
}
- if((iswspace(c2) || (punct_data & 0x8000) || IsBracket(c2) || (c2=='?') || (c2=='-') || Eof()))
+ // don't announce punctuation for the alternative text inside inside <audio> ... </audio>
+ if(c1 == 0xe000+'<') c1 = '<';
+ if(option_punctuation && iswpunct(c1) && (audio_text == 0))
{
- // note: (c2='?') is for when a smart-quote has been replaced by '?'
- buf[ix] = ' ';
- buf[ix+1] = 0;
+ // option is set to explicitly speak punctuation characters
+ // if a list of allowed punctuation has been set up, check whether the character is in it
+ if((option_punctuation == 1) || (wcschr(option_punctlist,c1) != NULL))
+ {
+ tr->phonemes_repeat_count = 0;
+ if((terminator = AnnouncePunctuation(tr, c1, &c2, buf, &ix, is_end_clause)) >= 0)
+ return(terminator);
+ announced_punctuation = c1;
+ }
+ }
- if((c1 == '.') && (cprev == '.'))
+ if((punct_data & PUNCT_SAY_NAME) && (announced_punctuation == 0))
+ {
+ // used for elipsis (and 3 dots) if a pronunciation for elipsis is given in *_list
+ char *p2;
+
+ p2 = &buf[ix];
+ sprintf(p2,"%s",LookupCharName(tr, c1, 1));
+ if(p2[0] != 0)
{
- c1 = 0x2026;
- punct = 9; // elipsis
+ ix += strlen(p2);
+ announced_punctuation = c1;
+ punct_data = punct_data & ~CLAUSE_BITS_INTONATION; // change intonation type to 0 (full-stop)
}
+ }
+ if(is_end_clause)
+ {
nl_count = 0;
- while(!Eof() && iswspace(c2))
+ c_next = c2;
+
+ if(iswspace(c_next))
{
- if(c2 == '\n')
- nl_count++;
- c2 = GetC(); // skip past space(s)
+ while(!Eof() && iswspace(c_next))
+ {
+ if(c_next == '\n')
+ nl_count++;
+ c_next = GetC(); // skip past space(s)
+ }
}
- if(!Eof())
+
+ if((c1 == '.') && (nl_count < 2))
{
- UngetC(c2);
+ punct_data |= CLAUSE_DOT;
}
- if((nl_count==0) && (c1 == '.'))
+ if(nl_count==0)
{
- if(iswdigit(cprev) && (tr->langopts.numbers & 0x10000) && islower(c2))
+ if((c1 == ',') && (cprev == '.') && (tr->translator_name == L('h','u')) && iswdigit(cprev2) && (iswdigit(c_next) || (iswlower2(c_next))))
{
- // dot after a number indicates an ordinal number
- c2 = '.';
- continue;
+ // lang=hu, fix for ordinal numbers, eg: "december 2., szerda", ignore ',' after ordinal number
+ c1 = CHAR_COMMA_BREAK;
+ is_end_clause = 0;
+ }
+
+ if(c1 == '.')
+ {
+ if((tr->langopts.numbers & NUM_ORDINAL_DOT) &&
+ (iswdigit(cprev) || (IsRomanU(cprev) && (IsRomanU(cprev2) || iswspace(cprev2))))) // lang=hu
+ {
+ // dot after a number indicates an ordinal number
+ if(!iswdigit(cprev))
+ {
+ is_end_clause = 0; // Roman number followed by dot
+ }
+ else
+ {
+ if (iswlower2(c_next) || (c_next=='-')) // hyphen is needed for lang-hu (eg. 2.-kal)
+ is_end_clause = 0; // only if followed by lower-case, (or if there is a XML tag)
+ }
+ }
+ else
+ if(c_next == '\'')
+ {
+ is_end_clause = 0; // eg. u.s.a.'s
+ }
+ if(iswlower2(c_next))
+ {
+ // next word has no capital letter, this dot is probably from an abbreviation
+// c1 = ' ';
+ is_end_clause = 0;
+ }
+ if(any_alnum==0)
+ {
+ // no letters or digits yet, so probably not a sentence terminator
+ // Here, dot is followed by space or bracket
+ c1 = ' ';
+ is_end_clause = 0;
+ }
}
- if(iswlower(c2))
+ else
{
- c2 = ' ';
- continue; // next word has no capital letter, this dot is probably from an abbreviation
+ if(any_alnum==0)
+ {
+ // no letters or digits yet, so probably not a sentence terminator
+ is_end_clause = 0;
+ }
}
- if(any_alnum==0)
+
+ if(is_end_clause && (c1 == '.') && (c_next == '<') && option_ssml)
{
- c2 = ' '; // no letters or digits yet, so probably not a sentence terminator
- continue;
+ // wait until after the end of the xml tag, then look for upper-case letter
+ is_end_clause = 0;
+ end_clause_index = ix;
+ end_clause_after_tag = punct_data;
}
}
- punct_data = punct_attributes[punct];
- if(nl_count > 1)
+ if(is_end_clause)
{
- if((punct_data == CLAUSE_QUESTION) || (punct_data == CLAUSE_EXCLAMATION))
- return(punct_data + 35); // with a longer pause
- return(CLAUSE_PARAGRAPH);
+ UngetC(c_next);
+ buf[ix] = ' ';
+ buf[ix+1] = 0;
+
+ if(iswdigit(cprev) && !IsAlpha(c_next)) // ????
+ {
+ punct_data &= ~CLAUSE_DOT;
+ }
+ if(nl_count > 1)
+ {
+ if((punct_data == CLAUSE_QUESTION) || (punct_data == CLAUSE_EXCLAMATION))
+ return(punct_data + 35); // with a longer pause
+ return(CLAUSE_PARAGRAPH);
+ }
+ return(punct_data); // only recognise punctuation if followed by a blank or bracket/quote
+ }
+ else
+ {
+ if(!Eof())
+ {
+ if(iswspace(c2))
+ UngetC(c_next);
+ }
}
- return(punct_data); // only recognise punctuation if followed by a blank or bracket/quote
}
}
if(speech_parameters[espeakSILENCE]==1)
continue;
+ if(c1 == announced_punctuation)
+ {
+ // This character has already been announced, so delete it so that it isn't spoken a second time.
+ // Unless it's a hyphen or apostrophe (which is used by TranslateClause() )
+ if(IsBracket(c1))
+ {
+ c1 = 0xe000 + '('; // Unicode private useage area. So TranslateRules() knows the bracket name has been spoken
+ }
+ else
+ if(c1 != '-')
+ {
+ c1 = ' ';
+ }
+ }
+
j = ix+1;
+
+ if(c1 == 0xe000 + '<') c1 = '<';
+
ix += utf8_out(c1,&buf[ix]); // buf[ix++] = c1;
if(!iswspace(c1) && !IsBracket(c1))
{
@@ -2379,10 +2909,11 @@ if(option_ssml) parag=1;
}
*charix_top = ix;
- if(((ix > (n_buf-20)) && !IsAlpha(c1) && !iswdigit(c1)) || (ix >= (n_buf-2)))
+ if(((ix > (n_buf-75)) && !IsAlpha(c1) && !iswdigit(c1)) || (ix >= (n_buf-4)))
{
// clause too long, getting near end of buffer, so break here
// try to break at a word boundary (unless we actually reach the end of buffer).
+ // (n_buf-4) is to allow for 3 bytes of multibyte character plus terminator.
buf[ix] = ' ';
buf[ix+1] = 0;
UngetC(c2);
@@ -2394,6 +2925,10 @@ if(option_ssml) parag=1;
{
ix += utf8_out(CHAR_EMPHASIS, &buf[ix]);
}
+ if(end_clause_after_tag)
+ {
+ RemoveChar(&buf[end_clause_index]); // delete clause-end punctiation
+ }
buf[ix] = ' ';
buf[ix+1] = 0;
return(CLAUSE_EOF); // end of file
@@ -2417,6 +2952,7 @@ void InitText2(void)
int param;
ungot_char = 0;
+ ungot_char2 = 0;
n_ssml_stack =1;
n_param_stack = 1;
@@ -2431,6 +2967,7 @@ void InitText2(void)
current_voice_id[0] = 0;
ignore_text = 0;
+ audio_text = 0;
clear_skipping_text = 0;
count_characters = -1;
sayas_mode = 0;
diff --git a/navit/support/espeak/setlengths.c b/navit/support/espeak/setlengths.c
index ed4421bf9..ca557c888 100755..100644
--- a/navit/support/espeak/setlengths.c
+++ b/navit/support/espeak/setlengths.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2011 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -29,76 +29,103 @@
#include "synthesize.h"
#include "voice.h"
#include "translate.h"
+#include "wavegen.h"
-extern int GetAmplitude(void);
+extern int saved_parameters[];
// convert from words-per-minute to internal speed factor
-static unsigned char speed_lookup[290] = {
- 250, 246, 243, 239, 236, // 80
- 233, 229, 226, 223, 220, // 85
- 217, 214, 211, 208, 205, // 90
- 202, 197, 194, 192, 190, // 95
- 187, 185, 183, 180, 178, // 100
- 176, 174, 172, 170, 168, // 105
- 166, 164, 161, 159, 158, // 110
- 156, 154, 152, 150, 148, // 115
- 146, 145, 143, 141, 137, // 120
- 136, 135, 133, 132, 131, // 125
- 129, 128, 127, 126, 125, // 130
- 124, 122, 121, 120, 119, // 135
- 117, 116, 115, 114, 113, // 140
- 112, 111, 110, 108, 107, // 145
- 106, 105, 104, 103, 102, // 150
- 101, 100, 99, 98, 97, // 155
- 96, 95, 93, 92, 92, // 160
- 91, 90, 89, 89, 88, // 165
- 87, 87, 86, 85, 85, // 170
- 84, 83, 83, 82, 81, // 175
- 80, 80, 79, 78, 78, // 180
- 77, 76, 76, 75, 73, // 185
- 72, 72, 71, 71, 70, // 190
- 70, 69, 69, 68, 67, // 195
- 67, 66, 66, 65, 65, // 200
- 64, 64, 63, 63, 62, // 205
- 62, 61, 60, 60, 59, // 210
- 59, 58, 58, 57, 57, // 215
- 56, 56, 55, 55, 55, // 220
- 54, 54, 53, 53, 52, // 225
- 52, 51, 51, 50, 50, // 230
- 49, 49, 49, 48, 48, // 235
- 47, 47, 46, 46, 46, // 240
- 45, 45, 44, 44, 43, // 245
- 43, 43, 42, 42, 41, // 250
- 41, 41, 40, 40, 39, // 255
- 39, 39, 38, 38, 38, // 260
- 37, 37, 37, 36, 36, // 265
- 35, 35, 35, 34, 34, // 270
- 34, 33, 33, 33, 32, // 275
- 32, 32, 32, 31, 31, // 280
- 31, 30, 30, 30, 29, // 285
- 29, 29, 29, 28, 28, // 290
- 28, 28, 27, 27, 27, // 295
- 26, 26, 26, 26, 25, // 300
- 25, 25, 22, 22, 22, // 305
- 22, 22, 22, 22, 22, // 310
- 21, 21, 21, 21, 21, // 315
- 21, 20, 20, 20, 20, // 320
- 20, 15, 15, 15, 15, // 325
- 15, 15, 15, 15, 16, // 330
- 16, 16, 16, 15, 15, // 335
- 15, 15, 15, 15, 15, // 340
- 15, 17, 17, 16, 16, // 345
- 15, 15, 14, 14, 13, // 350
- 13, 12, 12, 11, 11, // 355
- 10, 10, 9, 8, 8, // 360
- 7, 6, 5, 5, 4, // 365
+// Use this to calibrate speed for wpm 80-350
+static unsigned char speed_lookup[] = {
+ 255, 255, 255, 255, 255, // 80
+ 253, 249, 245, 242, 238, // 85
+ 235, 232, 228, 225, 222, // 90
+ 218, 216, 213, 210, 207, // 95
+ 204, 201, 198, 196, 193, // 100
+ 191, 188, 186, 183, 181, // 105
+ 179, 176, 174, 172, 169, // 110
+ 168, 165, 163, 161, 159, // 115
+ 158, 155, 153, 152, 150, // 120
+ 148, 146, 145, 143, 141, // 125
+ 139, 137, 136, 135, 133, // 130
+ 131, 130, 129, 127, 126, // 135
+ 124, 123, 122, 120, 119, // 140
+ 118, 117, 115, 114, 113, // 145
+ 112, 111, 110, 109, 107, // 150
+ 106, 105, 104, 103, 102, // 155
+ 101, 100, 99, 98, 97, // 160
+ 96, 95, 94, 93, 92, // 165
+ 91, 90, 89, 89, 88, // 170
+ 87, 86, 85, 84, 83, // 175
+ 82, 82, 81, 80, 80, // 180
+ 79, 78, 77, 76, 76, // 185
+ 75, 75, 74, 73, 72, // 190
+ 71, 71, 70, 69, 69, // 195
+ 68, 67, 67, 66, 66, // 200
+ 65, 64, 64, 63, 62, // 205
+ 62, 61, 61, 60, 59, // 210
+ 59, 58, 58, 57, 57, // 215
+ 56, 56, 55, 54, 54, // 220
+ 53, 53, 52, 52, 52, // 225
+ 51, 50, 50, 49, 49, // 230
+ 48, 48, 47, 47, 46, // 235
+ 46, 46, 45, 45, 44, // 240
+ 44, 44, 43, 43, 42, // 245
+ 41, 40, 40, 40, 39, // 250
+ 39, 39, 38, 38, 38, // 255
+ 37, 37, 37, 36, 36, // 260
+ 35, 35, 35, 35, 34, // 265
+ 34, 34, 33, 33, 33, // 270
+ 32, 32, 31, 31, 31, // 275
+ 30, 30, 30, 29, 29, // 280
+ 29, 29, 28, 28, 27, // 285
+ 27, 27, 27, 26, 26, // 290
+ 26, 26, 25, 25, 25, // 295
+ 24, 24, 24, 24, 23, // 300
+ 23, 23, 23, 22, 22, // 305
+ 22, 21, 21, 21, 21, // 310
+ 20, 20, 20, 20, 19, // 315
+ 19, 19, 18, 18, 17, // 320
+ 17, 17, 16, 16, 16, // 325
+ 16, 16, 16, 15, 15, // 330
+ 15, 15, 14, 14, 14, // 335
+ 13, 13, 13, 12, 12, // 340
+ 12, 12, 11, 11, 11, // 345
+ 11, 10, 10, 10, 9, // 350
+ 9, 9, 8, 8, 8, // 355
};
-// speed_factor2 adjustments for speeds 370 to 390
-static unsigned char faster[] = {
-114,112,110,109,107,105,104,102,100,98, // 370-379
-96,94,92,90,88,85,83,80,78,75,72 }; //380-390
+
+// speed_factor1 adjustments for speeds 350 to 374: pauses
+static unsigned char pause_factor_350[] = {
+22,22,22,22,22,22,22,21,21,21, // 350
+21,20,20,19,19,18,17,16,15,15, // 360
+15,15,15,15,15}; // 370
+
+// wav_factor adjustments for speeds 350 to 450
+// Use this to calibrate speed for wpm 350-450
+static unsigned char wav_factor_350[] = {
+ 120, 121, 120, 119, 119, // 350
+ 118, 118, 117, 116, 116, // 355
+ 115, 114, 113, 112, 112, // 360
+ 111, 111, 110, 109, 108, // 365
+ 107, 106, 106, 104, 103, // 370
+ 103, 102, 102, 102, 101, // 375
+ 101, 99, 98, 98, 97, // 380
+ 96, 96, 95, 94, 93, // 385
+ 91, 90, 91, 90, 89, // 390
+ 88, 86, 85, 86, 85, // 395
+ 85, 84, 82, 81, 80, // 400
+ 79, 77, 78, 78, 76, // 405
+ 77, 75, 75, 74, 73, // 410
+ 71, 72, 70, 69, 69, // 415
+ 69, 67, 65, 64, 63, // 420
+ 63, 63, 61, 61, 59, // 425
+ 59, 59, 58, 56, 57, // 430
+ 58, 56, 54, 53, 52, // 435
+ 52, 53, 52, 52, 50, // 440
+ 48, 47, 47, 45, 46, // 445
+ 45}; // 450
static int speed1 = 130;
static int speed2 = 121;
@@ -106,22 +133,96 @@ static int speed3 = 118;
+//#define TEST_SPEED
+
+#ifdef INCLUDE_SONIC
+
void SetSpeed(int control)
{//=======================
int x;
int s1;
int wpm;
int wpm2;
+ int wpm_value;
+ double sonic;
+
+ speed.loud_consonants = 0;
+ speed.min_sample_len = 450;
+ speed.lenmod_factor = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change
+ speed.lenmod2_factor = 100;
+ speed.min_pause = 5;
wpm = embedded_value[EMBED_S];
if(control == 2)
wpm = embedded_value[EMBED_S2];
- wpm2 = wpm;
- if(wpm > 369) wpm = 369;
- if(wpm < 80) wpm = 80;
+ wpm_value = wpm;
+
+ if(voice->speed_percent > 0)
+ {
+ wpm = (wpm * voice->speed_percent)/100;
+ }
+
+ if(control & 2)
+ {
+ DoSonicSpeed(1 * 1024);
+ }
+ if((wpm_value > 450) || ((wpm_value > speed.fast_settings[0]) && (wpm > 350)))
+ {
+ wpm2 = wpm;
+ wpm = 175;
+
+ // set special eSpeak speed parameters for Sonic use
+ // The eSpeak output will be speeded up by at least x2
+ x = 73;
+ if(control & 1)
+ {
+ speed1 = (x * voice->speedf1)/256;
+ speed2 = (x * voice->speedf2)/256;
+ speed3 = (x * voice->speedf3)/256;
+ }
+ if(control & 2)
+ {
+ sonic = ((double)wpm2)/wpm;
+ DoSonicSpeed((int)(sonic * 1024));
+ speed.pause_factor = 85;
+ speed.clause_pause_factor = 80;
+ speed.min_pause = 22;
+ speed.min_sample_len = 450*2;
+ speed.wav_factor = 211;
+ speed.lenmod_factor = 210;
+ speed.lenmod2_factor = 170;
+ }
+ return;
+ }
+
- x = speed_lookup[wpm-80];
+#ifdef TEST_SPEED
+ if(wpm > 1000)
+ {
+ // TESTING
+// test = wpm / 1000;
+ wpm = wpm % 1000;
+ }
+#endif
+
+ if(wpm > 450)
+ wpm = 450;
+
+ if(wpm > 360)
+ {
+ speed.loud_consonants = (wpm - 360) / 8;
+ }
+
+ wpm2 = wpm;
+ if(wpm > 359) wpm2 = 359;
+ if(wpm < 80) wpm2 = 80;
+ x = speed_lookup[wpm2-80];
+
+ if(wpm >= 380)
+ x = 7;
+ if(wpm >= 400)
+ x = 6;
if(control & 1)
{
@@ -130,45 +231,232 @@ void SetSpeed(int control)
speed1 = (x * voice->speedf1)/256;
speed2 = (x * voice->speedf2)/256;
speed3 = (x * voice->speedf3)/256;
+
+ if(x <= 7)
+ {
+ speed1 = x;
+ speed2 = speed3 = x - 1;
+ }
}
if(control & 2)
{
// these are used in synthesis file
+
+ if(wpm > 350)
+ {
+ speed.lenmod_factor = 85 - (wpm - 350) / 3;
+ speed.lenmod2_factor = 60 - (wpm - 350) / 8;
+ }
+ else
+ if(wpm > 250)
+ {
+ speed.lenmod_factor = 110 - (wpm - 250)/4;
+ speed.lenmod2_factor = 110 - (wpm - 250)/2;
+ }
+
s1 = (x * voice->speedf1)/256;
- speed.speed_factor1 = (256 * s1)/115; // full speed adjustment, used for pause length
-if(speed.speed_factor1 < 15)
- speed.speed_factor1 = 15;
+
if(wpm >= 170)
-// speed_factor2 = 100 + (166*s1)/128; // reduced speed adjustment, used for playing recorded sounds
- speed.speed_factor2 = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds
+ speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds
+ else
+ speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm
+
+ if(wpm >= 350)
+ {
+ speed.wav_factor = wav_factor_350[wpm-350];
+ }
+
+ if(wpm >= 390)
+ {
+ speed.min_sample_len = 450 - (wpm - 400)/2;
+ if(wpm > 440)
+ speed.min_sample_len = 420 - (wpm - 440);
+ }
+
+// adjust for different sample rates
+speed.min_sample_len = (speed.min_sample_len * samplerate_native) / 22050;
+
+ speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length
+ speed.clause_pause_factor = 0;
+
+ if(wpm > 430)
+ {
+ speed.pause_factor = 12;
+// speed.clause_pause_factor = 15;
+ }
else
- speed.speed_factor2 = 128 + (128*s1)/130; // = 215 at 170 wpm
+ if(wpm > 400)
+ {
+ speed.pause_factor = 13;
+// speed.clause_pause_factor = 15;
+ }
+ else
+ if(wpm > 374)
+ {
+ speed.pause_factor = 14;
+ }
+ else
+ if(wpm > 350)
+ {
+ speed.pause_factor = pause_factor_350[wpm - 350];
+ }
- if(wpm2 > 369)
+ if(speed.clause_pause_factor == 0)
{
- if(wpm2 > 390)
- wpm2 = 390;
- speed.speed_factor2 = faster[wpm2 - 370];
+ // restrict the reduction of pauses between clauses
+ if((speed.clause_pause_factor = speed.pause_factor) < 16)
+ speed.clause_pause_factor = 16;
}
}
+#ifdef TEST_SPEED
+//if(control==3)
+printf("%3d: speedf %d %d %d pause=%d %d wav=%d lenmod=%d %d\n",wpm,speed1,speed2,speed3, speed.pause_factor,speed.clause_pause_factor, speed.wav_factor,speed.lenmod_factor,speed.lenmod2_factor);
+#endif
+} // end of SetSpeed
+
+#else // not using sonic speed-up
+
+void SetSpeed(int control)
+{//=======================
+// This is the earlier version of SetSpeed() before sonic speed-up was added
+ int x;
+ int s1;
+ int wpm;
+ int wpm2;
+
+ speed.loud_consonants = 0;
speed.min_sample_len = 450;
- speed.speed_factor3 = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change
+ speed.lenmod_factor = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change
+ speed.lenmod2_factor = 100;
+
+ wpm = embedded_value[EMBED_S];
+ if(control == 2)
+ wpm = embedded_value[EMBED_S2];
- if(wpm2 >= 370)
+#ifdef TEST_SPEED
+ if(wpm > 1000)
{
// TESTING
- // use experimental fast settings if they have been specified in the Voice
- if(speed.fast_settings[0] > 0)
- speed.speed_factor1 = speed.fast_settings[0];
- if(speed.fast_settings[1] > 0)
- speed.speed_factor2 = speed.fast_settings[1];
- if(speed.fast_settings[2] > 0)
- speed.speed_factor3 = speed.fast_settings[2];
+ test = wpm / 1000;
+ wpm = wpm % 1000;
}
+#endif
+
+ if(voice->speed_percent > 0)
+ {
+ wpm = (wpm * voice->speed_percent)/100;
+ }
+ if(wpm > 450)
+ wpm = 450;
+
+ if(wpm > 360)
+ {
+ speed.loud_consonants = (wpm - 360) / 8;
+ }
+
+ wpm2 = wpm;
+ if(wpm > 359) wpm2 = 359;
+ if(wpm < 80) wpm2 = 80;
+ x = speed_lookup[wpm2-80];
+
+ if(wpm >= 380)
+ x = 7;
+ if(wpm >= 400)
+ x = 6;
+
+ if(control & 1)
+ {
+ // set speed factors for different syllable positions within a word
+ // these are used in CalcLengths()
+ speed1 = (x * voice->speedf1)/256;
+ speed2 = (x * voice->speedf2)/256;
+ speed3 = (x * voice->speedf3)/256;
+
+ if(x <= 7)
+ {
+ speed1 = x;
+ speed2 = speed3 = x - 1;
+ }
+ }
+
+ if(control & 2)
+ {
+ // these are used in synthesis file
+
+ if(wpm > 350)
+ {
+ speed.lenmod_factor = 85 - (wpm - 350) / 3;
+ speed.lenmod2_factor = 60 - (wpm - 350) / 8;
+ }
+ else
+ if(wpm > 250)
+ {
+ speed.lenmod_factor = 110 - (wpm - 250)/4;
+ speed.lenmod2_factor = 110 - (wpm - 250)/2;
+ }
+
+ s1 = (x * voice->speedf1)/256;
+
+ if(wpm >= 170)
+ speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds
+ else
+ speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm
+
+ if(wpm >= 350)
+ {
+ speed.wav_factor = wav_factor_350[wpm-350];
+ }
+
+ if(wpm >= 390)
+ {
+ speed.min_sample_len = 450 - (wpm - 400)/2;
+ if(wpm > 440)
+ speed.min_sample_len = 420 - (wpm - 440);
+ }
+
+ speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length
+ speed.clause_pause_factor = 0;
+
+ if(wpm > 430)
+ {
+ speed.pause_factor = 12;
+// speed.clause_pause_factor = 15;
+ }
+ else
+ if(wpm > 400)
+ {
+ speed.pause_factor = 13;
+// speed.clause_pause_factor = 15;
+ }
+ else
+ if(wpm > 374)
+ {
+ speed.pause_factor = 14;
+ }
+ else
+ if(wpm > 350)
+ {
+ speed.pause_factor = pause_factor_350[wpm - 350];
+ }
+
+ if(speed.clause_pause_factor == 0)
+ {
+ // restrict the reduction of pauses between clauses
+ if((speed.clause_pause_factor = speed.pause_factor) < 16)
+ speed.clause_pause_factor = 16;
+ }
+ }
+
+#ifdef TEST_SPEED
+//if(control==3)
+printf("%3d: speedf %d %d %d pause=%d %d wav=%d lenmod=%d %d\n",wpm,speed1,speed2,speed3, speed.pause_factor,speed.clause_pause_factor, speed.wav_factor,speed.lenmod_factor,speed.lenmod2_factor);
+#endif
} // end of SetSpeed
+#endif // of INCLUDE_SONIC
+
#ifdef deleted
void SetAmplitude(int amp)
@@ -177,7 +465,7 @@ void SetAmplitude(int amp)
if((amp >= 0) && (amp <= 20))
{
- option_amplitude = (amplitude_factor[amp] * 480)/256;
+ option_amplitude = (amplitude_factor[amp] * 480)/256;
}
}
#endif
@@ -201,6 +489,7 @@ void SetParameter(int parameter, int value, int relative)
}
}
param_stack[0].parameter[parameter] = new_value;
+ saved_parameters[parameter] = new_value;
switch(parameter)
{
@@ -285,14 +574,17 @@ void CalcLengths(Translator *tr)
int last_pitch = 0;
int pitch_start;
int length_mod;
+ int next2type;
int len;
int env2;
int end_of_clause;
int embedded_ix = 0;
int min_drop;
+ int pitch1;
int emphasized;
int tone_mod;
unsigned char *pitch_env=NULL;
+ PHONEME_DATA phdata_tone;
for(ix=1; ix<n_phoneme_list; ix++)
{
@@ -317,14 +609,14 @@ void CalcLengths(Translator *tr)
case phPAUSE:
last_pitch = 0;
break;
-
+
case phSTOP:
last_pitch = 0;
if(prev->type == phFRICATIVE)
- p->prepause = 20;
+ p->prepause = 25;
else
if((more_syllables > 0) || (stress < 4))
- p->prepause = 40;
+ p->prepause = 48;
else
p->prepause = 60;
@@ -342,13 +634,17 @@ void CalcLengths(Translator *tr)
break;
case phVFRICATIVE:
- if(next->type==phVOWEL)
- {
- pre_voiced = 1;
- } // drop through
case phFRICATIVE:
if(p->newword)
- p->prepause = 15;
+ {
+ if((prev->type == phVOWEL) && (p->ph->phflags & phNOPAUSE))
+ {
+ }
+ else
+ {
+ p->prepause = 15;
+ }
+ }
if(next->type==phPAUSE && prev->type==phNASAL && !(p->ph->phflags&phFORTIS))
p->prepause = 25;
@@ -356,6 +652,9 @@ void CalcLengths(Translator *tr)
if(prev->ph->phflags & phBRKAFTER)
p->prepause = 30;
+ if((tr->langopts.word_gap & 0x10) && (p->newword))
+ p->prepause = 30;
+
if((p->ph->phflags & phSIBILANT) && next->type==phSTOP && !next->newword)
{
if(prev->type == phVOWEL)
@@ -366,9 +665,17 @@ void CalcLengths(Translator *tr)
else
p->length = 256;
- if((tr->langopts.word_gap & 0x10) && (p->newword))
- p->prepause = 30;
-
+ if(type == phVFRICATIVE)
+ {
+ if(next->type==phVOWEL)
+ {
+ pre_voiced = 1;
+ }
+ if((prev->type==phVOWEL) || (prev->type == phLIQUID))
+ {
+ p->length = (255 + prev->length)/2;
+ }
+ }
break;
case phVSTOP:
@@ -382,8 +689,19 @@ void CalcLengths(Translator *tr)
p->prepause = 40;
- if((prev->type == phPAUSE) || (prev->type == phVOWEL)) // || (prev->ph->mnemonic == ('/'*256+'r')))
- p->prepause = 0;
+ if(prev->type == phVOWEL)
+ {
+ p->prepause = 0; // use murmur instead to link from the preceding vowel
+ }
+ else
+ if(prev->type == phPAUSE)
+ {
+ // reduce by the length of the preceding pause
+ if(prev->length < p->prepause)
+ p->prepause -= prev->length;
+ else
+ p->prepause = 0;
+ }
else
if(p->newword==0)
{
@@ -403,16 +721,19 @@ void CalcLengths(Translator *tr)
case phLIQUID:
case phNASAL:
- p->amp = tr->stress_amps[1]; // unless changed later
+ p->amp = tr->stress_amps[0]; // unless changed later
p->length = 256; // TEMPORARY
min_drop = 0;
-
+
if(p->newword)
{
if(prev->type==phLIQUID)
p->prepause = 25;
if(prev->type==phVOWEL)
- p->prepause = 12;
+ {
+ if(!(p->ph->phflags & phNOPAUSE))
+ p->prepause = 12;
+ }
}
if(next->type==phVOWEL)
@@ -420,43 +741,44 @@ void CalcLengths(Translator *tr)
pre_sonorant = 1;
}
else
- if((prev->type==phVOWEL) || (prev->type == phLIQUID))
{
- p->length = prev->length;
p->pitch2 = last_pitch;
- if(p->pitch2 < 7)
- p->pitch2 = 7;
- p->pitch1 = p->pitch2 - 8;
- p->env = PITCHfall;
- pre_voiced = 0;
-
- if(p->type == phLIQUID)
- {
- p->length = speed1;
-//p->pitch1 = p->pitch2 - 20; // post vocalic [r/]
- }
- if(next->type == phVSTOP)
+ if((prev->type==phVOWEL) || (prev->type == phLIQUID))
{
- p->length = (p->length * 160)/100;
- }
- if(next->type == phVFRICATIVE)
- {
- p->length = (p->length * 120)/100;
+ p->length = prev->length;
+
+ if(p->type == phLIQUID)
+ {
+ p->length = speed1;
+ }
+
+ if(next->type == phVSTOP)
+ {
+ p->length = (p->length * 160)/100;
+ }
+ if(next->type == phVFRICATIVE)
+ {
+ p->length = (p->length * 120)/100;
+ }
}
- }
- else
- {
- p->pitch2 = last_pitch;
- for(ix2=ix; ix2<n_phoneme_list; ix2++)
+ else
{
- if(phoneme_list[ix2].type == phVOWEL)
+ for(ix2=ix; ix2<n_phoneme_list; ix2++)
{
- p->pitch2 = phoneme_list[ix2].pitch2;
- break;
+ if(phoneme_list[ix2].type == phVOWEL)
+ {
+ p->pitch2 = phoneme_list[ix2].pitch2;
+ break;
+ }
}
}
- p->pitch1 = p->pitch2-8;
+
+ p->pitch1 = p->pitch2-16;
+ if(p->pitch2 < 16)
+ {
+ p->pitch1 = 0;
+ }
p->env = PITCHfall;
pre_voiced = 0;
}
@@ -469,6 +791,10 @@ void CalcLengths(Translator *tr)
if(stress > 7) stress = 7;
+if(stress <= 1)
+{
+ stress = stress ^ 1; // swap diminished and unstressed (until we swap stress_amps,stress_lengths in tr_languages)
+}
if(pre_sonorant)
p->amp = tr->stress_amps[stress]-1;
else
@@ -512,9 +838,17 @@ void CalcLengths(Translator *tr)
next3 = &phoneme_list[ix+4];
}
+ next2type = next2->ph->length_mod;
if(more_syllables==0)
{
- len = tr->langopts.length_mods0[next2->ph->length_mod *10+ next->ph->length_mod];
+ if(next->newword || next2->newword)
+ {
+ // don't use 2nd phoneme over a word boundary, unless it's a pause
+ if(next2type != 1)
+ next2type = 0;
+ }
+
+ len = tr->langopts.length_mods0[next2type *10+ next->ph->length_mod];
if((next->newword) && (tr->langopts.word_gap & 0x20))
{
@@ -526,7 +860,7 @@ void CalcLengths(Translator *tr)
}
else
{
- length_mod = tr->langopts.length_mods[next2->ph->length_mod *10+ next->ph->length_mod];
+ length_mod = tr->langopts.length_mods[next2type *10+ next->ph->length_mod];
if((next->type == phNASAL) && (next2->type == phSTOP || next2->type == phVSTOP) && (next3->ph->phflags & phFORTIS))
length_mod -= 15;
@@ -548,20 +882,20 @@ void CalcLengths(Translator *tr)
if(stress >= 7)
{
// tonic syllable, include a constant component so it doesn't decrease directly with speed
- length_mod += 20;
+ length_mod += tr->langopts.lengthen_tonic;
if(emphasized)
- length_mod += 10;
+ length_mod += (tr->langopts.lengthen_tonic/2);
}
else
if(emphasized)
{
- length_mod += 20;
+ length_mod += tr->langopts.lengthen_tonic;
}
-
+
if((len = tr->stress_lengths[stress]) == 0)
len = tr->stress_lengths[6];
- length_mod = (length_mod * len)/128;
+ length_mod = length_mod * len;
if(p->tone_ph != 0)
{
@@ -572,30 +906,45 @@ void CalcLengths(Translator *tr)
}
}
- if(end_of_clause == 2)
+
+ if((end_of_clause == 2) && !(tr->langopts.stress_flags & S_NO_EOC_LENGTHEN))
{
// this is the last syllable in the clause, lengthen it - more for short vowels
- len = p->ph->std_length;
- if(tr->langopts.stress_flags & 0x40000)
+ len = (p->ph->std_length * 2);
+ if(tr->langopts.stress_flags & S_EO_CLAUSE1)
len=200; // don't lengthen short vowels more than long vowels at end-of-clause
length_mod = length_mod * (256 + (280 - len)/3)/256;
}
+ if(length_mod > tr->langopts.max_lengthmod*speed1)
+ {
+ //limit the vowel length adjustment for some languages
+ length_mod = (tr->langopts.max_lengthmod*speed1);
+ }
+
+ length_mod = length_mod / 128;
+
if(p->type != phVOWEL)
{
length_mod = 256; // syllabic consonant
- min_drop = 8;
+ min_drop = 16;
}
p->length = length_mod;
+ if(p->env >= (N_ENVELOPE_DATA-1))
+ {
+ fprintf(stderr,"espeak: Bad intonation data\n");
+ p->env = 0;
+ }
+
// pre-vocalic part
// set last-pitch
- env2 = p->env;
- if(env2 > 1) env2++; // version for use with preceding semi-vowel
+ env2 = p->env + 1; // version for use with preceding semi-vowel
if(p->tone_ph != 0)
{
- pitch_env = LookupEnvelope(phoneme_tab[p->tone_ph]->spect);
+ InterpretPhoneme2(p->tone_ph, &phdata_tone);
+ pitch_env = GetEnvelope(phdata_tone.pitch_env);
}
else
{
@@ -607,11 +956,11 @@ if(p->type != phVOWEL)
if(pre_sonorant || pre_voiced)
{
// set pitch for pre-vocalic part
- if(pitch_start == 1024)
+ if(pitch_start == 255)
last_pitch = pitch_start; // pitch is not set
- if(pitch_start - last_pitch > 8) // was 9
- last_pitch = pitch_start - 8;
+ if(pitch_start - last_pitch > 16)
+ last_pitch = pitch_start - 16;
prev->pitch1 = last_pitch;
prev->pitch2 = pitch_start;
@@ -636,11 +985,11 @@ if(p->type != phVOWEL)
next->synthflags &= ~SFLAG_SEQCONTINUE;
if(next->type == phNASAL && next2->type != phVOWEL)
next->synthflags |= SFLAG_SEQCONTINUE;
-
+
if(next->type == phLIQUID)
{
next->synthflags |= SFLAG_SEQCONTINUE;
-
+
if(next2->type == phVOWEL)
{
next->synthflags &= ~SFLAG_SEQCONTINUE;
@@ -658,9 +1007,10 @@ if(p->type != phVOWEL)
if((min_drop > 0) && ((p->pitch2 - p->pitch1) < min_drop))
{
- p->pitch1 = p->pitch2 - min_drop;
- if(p->pitch1 < 0)
- p->pitch1 = 0;
+ pitch1 = p->pitch2 - min_drop;
+ if(pitch1 < 0)
+ pitch1 = 0;
+ p->pitch1 = pitch1;
}
last_pitch = p->pitch1 + ((p->pitch2-p->pitch1)*envelope_data[p->env][127])/256;
diff --git a/navit/support/espeak/sintab.h b/navit/support/espeak/sintab.h
index 08fc18f31..08fc18f31 100755..100644
--- a/navit/support/espeak/sintab.h
+++ b/navit/support/espeak/sintab.h
diff --git a/navit/support/espeak/sonic.c b/navit/support/espeak/sonic.c
new file mode 100644
index 000000000..0627dee2c
--- /dev/null
+++ b/navit/support/espeak/sonic.c
@@ -0,0 +1,974 @@
+/* Sonic library
+ Copyright 2010
+ Bill Cox
+ This file is part of the Sonic Library.
+
+ The Sonic Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include "StdAfx.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "speech.h"
+#include "sonic.h"
+#ifdef INCLUDE_SONIC
+
+struct sonicStreamStruct {
+ short *inputBuffer;
+ short *outputBuffer;
+ short *pitchBuffer;
+ short *downSampleBuffer;
+ float speed;
+ float volume;
+ float pitch;
+ int numChannels;
+ int inputBufferSize;
+ int pitchBufferSize;
+ int outputBufferSize;
+ int numInputSamples;
+ int numOutputSamples;
+ int numPitchSamples;
+ int minPeriod;
+ int maxPeriod;
+ int maxRequired;
+ int remainingInputToCopy;
+ int sampleRate;
+ int prevPeriod;
+ int prevMaxDiff;
+ int prevMinDiff;
+};
+
+#if 0
+/* Just used for debugging */
+static void sonicMSG(char *format, ...)
+{
+ char buffer[4096];
+ va_list ap;
+ FILE *file;
+
+ va_start(ap, format);
+ vsprintf((char *)buffer, (char *)format, ap);
+ va_end(ap);
+ file=fopen("/tmp/sonic.log", "a");
+ fprintf(file, "%s", buffer);
+ fclose(file);
+}
+#endif
+
+/* Scale the samples by the factor. */
+static void scaleSamples(
+ short *samples,
+ int numSamples,
+ float volume)
+{
+ int fixedPointVolume = volume*4096.0f;
+ int value;
+
+ while(numSamples--) {
+ value = (*samples*fixedPointVolume) >> 12;
+ if(value > 32767) {
+ value = 32767;
+ } else if(value < -32767) {
+ value = -32767;
+ }
+ *samples++ = value;
+ }
+}
+
+/* Get the speed of the stream. */
+float sonicGetSpeed(
+ sonicStream stream)
+{
+ return stream->speed;
+}
+
+/* Set the speed of the stream. */
+void sonicSetSpeed(
+ sonicStream stream,
+ float speed)
+{
+ stream->speed = speed;
+}
+
+/* Get the pitch of the stream. */
+float sonicGetPitch(
+ sonicStream stream)
+{
+ return stream->pitch;
+}
+
+/* Set the pitch of the stream. */
+void sonicSetPitch(
+ sonicStream stream,
+ float pitch)
+{
+ stream->pitch = pitch;
+}
+
+/* Get the scaling factor of the stream. */
+float sonicGetVolume(
+ sonicStream stream)
+{
+ return stream->volume;
+}
+
+/* Set the scaling factor of the stream. */
+void sonicSetVolume(
+ sonicStream stream,
+ float volume)
+{
+ stream->volume = volume;
+}
+
+/* Get the sample rate of the stream. */
+int sonicGetSampleRate(
+ sonicStream stream)
+{
+ return stream->sampleRate;
+}
+
+/* Get the number of channels. */
+int sonicGetNumChannels(
+ sonicStream stream)
+{
+ return stream->numChannels;
+}
+
+/* Destroy the sonic stream. */
+void sonicDestroyStream(
+ sonicStream stream)
+{
+ if(stream->inputBuffer != NULL) {
+ free(stream->inputBuffer);
+ }
+ if(stream->outputBuffer != NULL) {
+ free(stream->outputBuffer);
+ }
+ if(stream->pitchBuffer != NULL) {
+ free(stream->pitchBuffer);
+ }
+ if(stream->downSampleBuffer != NULL) {
+ free(stream->downSampleBuffer);
+ }
+ free(stream);
+}
+
+/* Create a sonic stream. Return NULL only if we are out of memory and cannot
+ allocate the stream. */
+sonicStream sonicCreateStream(
+ int sampleRate,
+ int numChannels)
+{
+ sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
+ int minPeriod = sampleRate/SONIC_MAX_PITCH;
+ int maxPeriod = sampleRate/SONIC_MIN_PITCH;
+ int maxRequired = 2*maxPeriod;
+
+ if(stream == NULL) {
+ return NULL;
+ }
+ stream->inputBufferSize = maxRequired;
+ stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+ if(stream->inputBuffer == NULL) {
+ sonicDestroyStream(stream);
+ return NULL;
+ }
+ stream->outputBufferSize = maxRequired;
+ stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+ if(stream->outputBuffer == NULL) {
+ sonicDestroyStream(stream);
+ return NULL;
+ }
+ stream->pitchBufferSize = maxRequired;
+ stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+ if(stream->pitchBuffer == NULL) {
+ sonicDestroyStream(stream);
+ return NULL;
+ }
+ stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
+ stream->speed = 1.0f;
+ stream->pitch = 1.0f;
+ stream->volume = 1.0f;
+ stream->sampleRate = sampleRate;
+ stream->numChannels = numChannels;
+ stream->minPeriod = minPeriod;
+ stream->maxPeriod = maxPeriod;
+ stream->maxRequired = maxRequired;
+ return stream;
+}
+
+/* Enlarge the output buffer if needed. */
+static int enlargeOutputBufferIfNeeded(
+ sonicStream stream,
+ int numSamples)
+{
+ if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
+ stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
+ stream->outputBuffer = (short *)realloc(stream->outputBuffer,
+ stream->outputBufferSize*sizeof(short)*stream->numChannels);
+ if(stream->outputBuffer == NULL) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Enlarge the input buffer if needed. */
+static int enlargeInputBufferIfNeeded(
+ sonicStream stream,
+ int numSamples)
+{
+ if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
+ stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
+ stream->inputBuffer = (short *)realloc(stream->inputBuffer,
+ stream->inputBufferSize*sizeof(short)*stream->numChannels);
+ if(stream->inputBuffer == NULL) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addFloatSamplesToInputBuffer(
+ sonicStream stream,
+ float *samples,
+ int numSamples)
+{
+ short *buffer;
+ int count = numSamples*stream->numChannels;
+
+ if(numSamples == 0) {
+ return 1;
+ }
+ if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+ return 0;
+ }
+ buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
+ while(count--) {
+ *buffer++ = (*samples++)*32767.0f;
+ }
+ stream->numInputSamples += numSamples;
+ return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addShortSamplesToInputBuffer(
+ sonicStream stream,
+ short *samples,
+ int numSamples)
+{
+ if(numSamples == 0) {
+ return 1;
+ }
+ if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+ return 0;
+ }
+ memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
+ numSamples*sizeof(short)*stream->numChannels);
+ stream->numInputSamples += numSamples;
+ return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addUnsignedCharSamplesToInputBuffer(
+ sonicStream stream,
+ unsigned char *samples,
+ int numSamples)
+{
+ short *buffer;
+ int count = numSamples*stream->numChannels;
+
+ if(numSamples == 0) {
+ return 1;
+ }
+ if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+ return 0;
+ }
+ buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
+ while(count--) {
+ *buffer++ = (*samples++ - 128) << 8;
+ }
+ stream->numInputSamples += numSamples;
+ return 1;
+}
+
+/* Remove input samples that we have already processed. */
+static void removeInputSamples(
+ sonicStream stream,
+ int position)
+{
+ int remainingSamples = stream->numInputSamples - position;
+
+ if(remainingSamples > 0) {
+ memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
+ remainingSamples*sizeof(short)*stream->numChannels);
+ }
+ stream->numInputSamples = remainingSamples;
+}
+
+/* Just copy from the array to the output buffer */
+static int copyToOutput(
+ sonicStream stream,
+ short *samples,
+ int numSamples)
+{
+ if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
+ return 0;
+ }
+ memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
+ samples, numSamples*sizeof(short)*stream->numChannels);
+ stream->numOutputSamples += numSamples;
+ return numSamples;
+}
+
+/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
+ resize the output buffer. Otherwise, return numSamples */
+static int copyInputToOutput(
+ sonicStream stream,
+ int position)
+{
+ int numSamples = stream->remainingInputToCopy;
+
+ if(numSamples > stream->maxRequired) {
+ numSamples = stream->maxRequired;
+ }
+ if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
+ numSamples)) {
+ return 0;
+ }
+ stream->remainingInputToCopy -= numSamples;
+ return numSamples;
+}
+
+/* Read data out of the stream. Sometimes no data will be available, and zero
+ is returned, which is not an error condition. */
+int sonicReadFloatFromStream(
+ sonicStream stream,
+ float *samples,
+ int maxSamples)
+{
+ int numSamples = stream->numOutputSamples;
+ int remainingSamples = 0;
+ short *buffer;
+ int count;
+
+ if(numSamples == 0) {
+ return 0;
+ }
+ if(numSamples > maxSamples) {
+ remainingSamples = numSamples - maxSamples;
+ numSamples = maxSamples;
+ }
+ buffer = stream->outputBuffer;
+ count = numSamples*stream->numChannels;
+ while(count--) {
+ *samples++ = (*buffer++)/32767.0f;
+ }
+ if(remainingSamples > 0) {
+ memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+ remainingSamples*sizeof(short)*stream->numChannels);
+ }
+ stream->numOutputSamples = remainingSamples;
+ return numSamples;
+}
+
+/* Read short data out of the stream. Sometimes no data will be available, and zero
+ is returned, which is not an error condition. */
+int sonicReadShortFromStream(
+ sonicStream stream,
+ short *samples,
+ int maxSamples)
+{
+ int numSamples = stream->numOutputSamples;
+ int remainingSamples = 0;
+
+ if(numSamples == 0) {
+ return 0;
+ }
+ if(numSamples > maxSamples) {
+ remainingSamples = numSamples - maxSamples;
+ numSamples = maxSamples;
+ }
+ memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
+ if(remainingSamples > 0) {
+ memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+ remainingSamples*sizeof(short)*stream->numChannels);
+ }
+ stream->numOutputSamples = remainingSamples;
+ return numSamples;
+}
+
+/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
+ is returned, which is not an error condition. */
+int sonicReadUnsignedCharFromStream(
+ sonicStream stream,
+ unsigned char *samples,
+ int maxSamples)
+{
+ int numSamples = stream->numOutputSamples;
+ int remainingSamples = 0;
+ short *buffer;
+ int count;
+
+ if(numSamples == 0) {
+ return 0;
+ }
+ if(numSamples > maxSamples) {
+ remainingSamples = numSamples - maxSamples;
+ numSamples = maxSamples;
+ }
+ buffer = stream->outputBuffer;
+ count = numSamples*stream->numChannels;
+ while(count--) {
+ *samples++ = (char)((*buffer++) >> 8) + 128;
+ }
+ if(remainingSamples > 0) {
+ memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+ remainingSamples*sizeof(short)*stream->numChannels);
+ }
+ stream->numOutputSamples = remainingSamples;
+ return numSamples;
+}
+
+/* Force the sonic stream to generate output using whatever data it currently
+ has. No extra delay will be added to the output, but flushing in the middle of
+ words could introduce distortion. */
+int sonicFlushStream(
+ sonicStream stream)
+{
+ int maxRequired = stream->maxRequired;
+ int numSamples = stream->numInputSamples;
+ int remainingSpace, numOutputSamples, expectedSamples;
+
+ if(numSamples == 0) {
+ return 1;
+ }
+ if(numSamples >= maxRequired && !sonicWriteShortToStream(stream, NULL, 0)) {
+ return 0;
+ }
+ numSamples = stream->numInputSamples; /* Now numSamples < maxRequired */
+ if(numSamples == 0) {
+ return 1;
+ }
+ remainingSpace = maxRequired - numSamples;
+ memset(stream->inputBuffer + numSamples*stream->numChannels, 0,
+ remainingSpace*sizeof(short)*stream->numChannels);
+ stream->numInputSamples = maxRequired;
+ numOutputSamples = stream->numOutputSamples;
+ if(!sonicWriteShortToStream(stream, NULL, 0)) {
+ return 0;
+ }
+ /* Throw away any extra samples we generated due to the silence we added */
+ expectedSamples = (int)(numSamples*stream->speed + 0.5);
+ if(stream->numOutputSamples > numOutputSamples + expectedSamples) {
+ stream->numOutputSamples = numOutputSamples + expectedSamples;
+ }
+ return 1;
+}
+
+/* Return the number of samples in the output buffer */
+int sonicSamplesAvailable(
+ sonicStream stream)
+{
+ return stream->numOutputSamples;
+}
+
+/* If skip is greater than one, average skip samples togther and write them to
+ the down-sample buffer. If numChannels is greater than one, mix the channels
+ together as we down sample. */
+static void downSampleInput(
+ sonicStream stream,
+ short *samples,
+ int skip)
+{
+ int numSamples = stream->maxRequired/skip;
+ int samplesPerValue = stream->numChannels*skip;
+ int i, j;
+ int value;
+ short *downSamples = stream->downSampleBuffer;
+
+ for(i = 0; i < numSamples; i++) {
+ value = 0;
+ for(j = 0; j < samplesPerValue; j++) {
+ value += *samples++;
+ }
+ value /= samplesPerValue;
+ *downSamples++ = value;
+ }
+}
+
+/* Find the best frequency match in the range, and given a sample skip multiple.
+ For now, just find the pitch of the first channel. */
+static int findPitchPeriodInRange(
+ short *samples,
+ int minPeriod,
+ int maxPeriod,
+ int *retMinDiff,
+ int *retMaxDiff)
+{
+ int period, bestPeriod = 0;
+ short *s, *p, sVal, pVal;
+ unsigned long diff, minDiff = 1, maxDiff = 0;
+ int i;
+
+ for(period = minPeriod; period <= maxPeriod; period++) {
+ diff = 0;
+ s = samples;
+ p = samples + period;
+ for(i = 0; i < period; i++) {
+ sVal = *s++;
+ pVal = *p++;
+ diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
+ (unsigned short)(pVal - sVal);
+ }
+ /* Note that the highest number of samples we add into diff will be less
+ than 256, since we skip samples. Thus, diff is a 24 bit number, and
+ we can safely multiply by numSamples without overflow */
+ if(diff*bestPeriod < minDiff*period) {
+ minDiff = diff;
+ bestPeriod = period;
+ }
+ if(diff*bestPeriod > maxDiff*period) {
+ maxDiff = diff;
+ }
+ }
+ *retMinDiff = minDiff;
+ *retMaxDiff = maxDiff;
+ return bestPeriod;
+}
+
+/* At abrupt ends of voiced words, we can have pitch periods that are better
+ aproximated by the previous pitch period estimate. Try to detect this case. */
+static int prevPeriodBetter(
+ sonicStream stream,
+ int period,
+ int minDiff,
+ int maxDiff)
+{
+ if(maxDiff*3/2 < stream->prevMaxDiff && (maxDiff*3.0f)*stream->prevMinDiff <
+ (float)stream->prevMaxDiff*minDiff*2) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Find the pitch period. This is a critical step, and we may have to try
+ multiple ways to get a good answer. This version uses AMDF. To improve
+ speed, we down sample by an integer factor get in the 11KHz range, and then
+ do it again with a narrower frequency range without down sampling */
+static int findPitchPeriod(
+ sonicStream stream,
+ short *samples)
+{
+ int minPeriod = stream->minPeriod;
+ int maxPeriod = stream->maxPeriod;
+ int sampleRate = stream->sampleRate;
+ int minDiff, maxDiff, retPeriod;
+ int skip = 1;
+ int period;
+
+ if(sampleRate > SONIC_AMDF_FREQ) {
+ skip = sampleRate/SONIC_AMDF_FREQ;
+ }
+ if(stream->numChannels == 1 && skip == 1) {
+ period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
+ } else {
+ downSampleInput(stream, samples, skip);
+ period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
+ maxPeriod/skip, &minDiff, &maxDiff);
+ if(skip != 1) {
+ period *= skip;
+ minPeriod = period - (skip << 2);
+ maxPeriod = period + (skip << 2);
+ if(minPeriod < stream->minPeriod) {
+ minPeriod = stream->minPeriod;
+ }
+ if(maxPeriod > stream->maxPeriod) {
+ maxPeriod = stream->maxPeriod;
+ }
+ if(stream->numChannels == 1) {
+ period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
+ &minDiff, &maxDiff);
+ } else {
+ downSampleInput(stream, samples, 1);
+ period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
+ maxPeriod, &minDiff, &maxDiff);
+ }
+ }
+ }
+ if(prevPeriodBetter(stream, period, minDiff, maxDiff)) {
+ retPeriod = stream->prevPeriod;
+ } else {
+ retPeriod = period;
+ }
+ stream->prevMinDiff = minDiff;
+ stream->prevMaxDiff = maxDiff;
+ stream->prevPeriod = period;
+ return retPeriod;
+}
+
+/* Overlap two sound segments, ramp the volume of one down, while ramping the
+ other one from zero up, and add them, storing the result at the output. */
+static void overlapAdd(
+ int numSamples,
+ int numChannels,
+ short *out,
+ short *rampDown,
+ short *rampUp)
+{
+ short *o, *u, *d;
+ int i, t;
+
+ for(i = 0; i < numChannels; i++) {
+ o = out + i;
+ u = rampUp + i;
+ d = rampDown + i;
+ for(t = 0; t < numSamples; t++) {
+ *o = (*d*(numSamples - t) + *u*t)/numSamples;
+ o += numChannels;
+ d += numChannels;
+ u += numChannels;
+ }
+ }
+}
+
+/* Overlap two sound segments, ramp the volume of one down, while ramping the
+ other one from zero up, and add them, storing the result at the output. */
+static void overlapAddWithSeparation(
+ int numSamples,
+ int numChannels,
+ int separation,
+ short *out,
+ short *rampDown,
+ short *rampUp)
+{
+ short *o, *u, *d;
+ int i, t;
+
+ for(i = 0; i < numChannels; i++) {
+ o = out + i;
+ u = rampUp + i;
+ d = rampDown + i;
+ for(t = 0; t < numSamples + separation; t++) {
+ if(t < separation) {
+ *o = *d*(numSamples - t)/numSamples;
+ d += numChannels;
+ } else if(t < numSamples) {
+ *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
+ d += numChannels;
+ u += numChannels;
+ } else {
+ *o = *u*(t - separation)/numSamples;
+ u += numChannels;
+ }
+ o += numChannels;
+ }
+ }
+}
+
+/* Just move the new samples in the output buffer to the pitch bufer */
+static int moveNewSamplesToPitchBuffer(
+ sonicStream stream,
+ int originalNumOutputSamples)
+{
+ int numSamples = stream->numOutputSamples - originalNumOutputSamples;
+ int numChannels = stream->numChannels;
+
+ if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
+ stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
+ stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
+ stream->pitchBufferSize*sizeof(short)*numChannels);
+ if(stream->pitchBuffer == NULL) {
+ return 0;
+ }
+ }
+ memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
+ stream->outputBuffer + originalNumOutputSamples*numChannels,
+ numSamples*sizeof(short)*numChannels);
+ stream->numOutputSamples = originalNumOutputSamples;
+ stream->numPitchSamples += numSamples;
+ return 1;
+}
+
+/* Remove processed samples from the pitch buffer. */
+static void removePitchSamples(
+ sonicStream stream,
+ int numSamples)
+{
+ int numChannels = stream->numChannels;
+ short *source = stream->pitchBuffer + numSamples*numChannels;
+
+ if(numSamples == 0) {
+ return;
+ }
+ if(numSamples != stream->numPitchSamples) {
+ memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
+ numSamples)*sizeof(short)*numChannels);
+ }
+ stream->numPitchSamples -= numSamples;
+}
+
+/* Change the pitch. The latency this introduces could be reduced by looking at
+ past samples to determine pitch, rather than future. */
+static int adjustPitch(
+ sonicStream stream,
+ int originalNumOutputSamples)
+{
+ float pitch = stream->pitch;
+ int numChannels = stream->numChannels;
+ int period, newPeriod, separation;
+ int position = 0;
+ short *out, *rampDown, *rampUp;
+
+ if(stream->numOutputSamples == originalNumOutputSamples) {
+ return 1;
+ }
+ if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
+ return 0;
+ }
+ while(stream->numPitchSamples - position >= stream->maxRequired) {
+ period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels);
+ newPeriod = period/pitch;
+ if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
+ return 0;
+ }
+ out = stream->outputBuffer + stream->numOutputSamples*numChannels;
+ if(pitch >= 1.0f) {
+ rampDown = stream->pitchBuffer + position*numChannels;
+ rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
+ overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
+ } else {
+ rampDown = stream->pitchBuffer + position*numChannels;
+ rampUp = stream->pitchBuffer + position*numChannels;
+ separation = newPeriod - period;
+ overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
+ }
+ stream->numOutputSamples += newPeriod;
+ position += period;
+ }
+ removePitchSamples(stream, position);
+ return 1;
+}
+
+/* Skip over a pitch period, and copy period/speed samples to the output */
+static int skipPitchPeriod(
+ sonicStream stream,
+ short *samples,
+ float speed,
+ int period)
+{
+ long newSamples;
+ int numChannels = stream->numChannels;
+
+ if(speed >= 2.0f) {
+ newSamples = period/(speed - 1.0f);
+ } else if(speed > 1.0f) {
+ newSamples = period;
+ stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
+ }
+ if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
+ return 0;
+ }
+ overlapAdd(newSamples, numChannels, stream->outputBuffer +
+ stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
+ stream->numOutputSamples += newSamples;
+ return newSamples;
+}
+
+/* Insert a pitch period, and determine how much input to copy directly. */
+static int insertPitchPeriod(
+ sonicStream stream,
+ short *samples,
+ float speed,
+ int period)
+{
+ long newSamples;
+ short *out;
+ int numChannels = stream->numChannels;
+
+ if(speed < 0.5f) {
+ newSamples = period*speed/(1.0f - speed);
+ } else {
+ newSamples = period;
+ stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
+ }
+ if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
+ return 0;
+ }
+ out = stream->outputBuffer + stream->numOutputSamples*numChannels;
+ memcpy(out, samples, period*sizeof(short)*numChannels);
+ out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
+ overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
+ stream->numOutputSamples += period + newSamples;
+ return newSamples;
+}
+
+/* Resample as many pitch periods as we have buffered on the input. Return 0 if
+ we fail to resize an input or output buffer. Also scale the output by the volume. */
+static int changeSpeed(
+ sonicStream stream,
+ float speed)
+{
+ short *samples;
+ int numSamples = stream->numInputSamples;
+ int position = 0, period, newSamples;
+ int maxRequired = stream->maxRequired;
+
+ if(stream->numInputSamples < maxRequired) {
+ return 1;
+ }
+ do {
+ if(stream->remainingInputToCopy > 0) {
+ newSamples = copyInputToOutput(stream, position);
+ position += newSamples;
+ } else {
+ samples = stream->inputBuffer + position*stream->numChannels;
+ period = findPitchPeriod(stream, samples);
+ if(speed > 1.0) {
+ newSamples = skipPitchPeriod(stream, samples, speed, period);
+ position += period + newSamples;
+ } else {
+ newSamples = insertPitchPeriod(stream, samples, speed, period);
+ position += newSamples;
+ }
+ }
+ if(newSamples == 0) {
+ return 0; /* Failed to resize output buffer */
+ }
+ } while(position + maxRequired <= numSamples);
+ removeInputSamples(stream, position);
+ return 1;
+}
+
+/* Resample as many pitch periods as we have buffered on the input. Return 0 if
+ we fail to resize an input or output buffer. Also scale the output by the volume. */
+static int processStreamInput(
+ sonicStream stream)
+{
+ int originalNumOutputSamples = stream->numOutputSamples;
+ float speed = stream->speed/stream->pitch;
+
+ if(speed > 1.00001 || speed < 0.99999) {
+ changeSpeed(stream, speed);
+ } else {
+ if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
+ return 0;
+ }
+ stream->numInputSamples = 0;
+ }
+ if(stream->pitch != 1.0f) {
+ if(!adjustPitch(stream, originalNumOutputSamples)) {
+ return 0;
+ }
+ }
+ if(stream->volume != 1.0f) {
+ /* Adjust output volume. */
+ scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
+ (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
+ stream->volume);
+ }
+ return 1;
+}
+
+/* Write floating point data to the input buffer and process it. */
+int sonicWriteFloatToStream(
+ sonicStream stream,
+ float *samples,
+ int numSamples)
+{
+ if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
+ return 0;
+ }
+ return processStreamInput(stream);
+}
+
+/* Simple wrapper around sonicWriteFloatToStream that does the short to float
+ conversion for you. */
+int sonicWriteShortToStream(
+ sonicStream stream,
+ short *samples,
+ int numSamples)
+{
+ if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
+ return 0;
+ }
+ return processStreamInput(stream);
+}
+
+/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
+ conversion for you. */
+int sonicWriteUnsignedCharToStream(
+ sonicStream stream,
+ unsigned char *samples,
+ int numSamples)
+{
+ if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
+ return 0;
+ }
+ return processStreamInput(stream);
+}
+
+/* This is a non-stream oriented interface to just change the speed of a sound sample */
+int sonicChangeFloatSpeed(
+ float *samples,
+ int numSamples,
+ float speed,
+ float pitch,
+ float volume,
+ int sampleRate,
+ int numChannels)
+{
+ sonicStream stream = sonicCreateStream(sampleRate, numChannels);
+
+ sonicSetSpeed(stream, speed);
+ sonicSetPitch(stream, pitch);
+ sonicSetVolume(stream, volume);
+ sonicWriteFloatToStream(stream, samples, numSamples);
+ sonicFlushStream(stream);
+ numSamples = sonicSamplesAvailable(stream);
+ sonicReadFloatFromStream(stream, samples, numSamples);
+ sonicDestroyStream(stream);
+ return numSamples;
+}
+
+/* This is a non-stream oriented interface to just change the speed of a sound sample */
+int sonicChangeShortSpeed(
+ short *samples,
+ int numSamples,
+ float speed,
+ float pitch,
+ float volume,
+ int sampleRate,
+ int numChannels)
+{
+ sonicStream stream = sonicCreateStream(sampleRate, numChannels);
+
+ sonicSetSpeed(stream, speed);
+ sonicSetPitch(stream, pitch);
+ sonicSetVolume(stream, volume);
+ sonicWriteShortToStream(stream, samples, numSamples);
+ sonicFlushStream(stream);
+ numSamples = sonicSamplesAvailable(stream);
+ sonicReadShortFromStream(stream, samples, numSamples);
+ sonicDestroyStream(stream);
+ return numSamples;
+}
+#endif // INCLUDE_SONIC
diff --git a/navit/support/espeak/sonic.h b/navit/support/espeak/sonic.h
new file mode 100644
index 000000000..0afe7f85f
--- /dev/null
+++ b/navit/support/espeak/sonic.h
@@ -0,0 +1,138 @@
+/* Sonic library
+ Copyright 2010
+ Bill Cox
+ This file is part of the Sonic Library.
+
+ The Sonic Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/*
+The Sonic Library implements Pitch Based Resampling, which is a new algorithm
+invented by Bill Cox for the specific purpose of speeding up speech by high
+factors at high quality. It generates smooth speech at speed up factors as high
+as 6X, possibly more. It is also capable of slowing down speech, and generates
+high quality results regardless of the speed up or slow down factor. For
+speeding up speech by 2X or more, the following equation is used:
+
+ newSamples = period/(speed - 1.0)
+ scale = 1.0/newSamples;
+
+where period is the current pitch period, determined using AMDF or any other
+pitch estimator, and speed is the speedup factor. If the current position in
+the input stream is pointed to by "samples", and the current output stream
+position is pointed to by "out", then newSamples number of samples can be
+generated with:
+
+ out[t] = (samples[t]*(newSamples - t) + samples[t + period]*t)/newSamples;
+
+where t = 0 to newSamples - 1.
+
+For speed factors < 2X, an algorithm similar to PICOLA is used. The above
+algorithm is first used to double the speed of one pitch period. Then, enough
+input is directly copied from the input to the output to achieve the desired
+speed up facter, where 1.0 < speed < 2.0. The amount of data copied is derived:
+
+ speed = (2*period + length)/(period + length)
+ speed*length + speed*period = 2*period + length
+ length(speed - 1) = 2*period - speed*period
+ length = period*(2 - speed)/(speed - 1)
+
+For slowing down speech where 0.5 < speed < 1.0, a pitch period is inserted into
+the output twice, and length of input is copied from the input to the output
+until the output desired speed is reached. The length of data copied is:
+
+ length = period*(speed - 0.5)/(1 - speed)
+
+For slow down factors between 0.5 and 0.5, no data is copied, and an algorithm
+similar to high speed factors is used.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This specifies the range of voice pitches we try to match.
+ Note that if we go lower than 65, we could overflow in findPitchInRange */
+#define SONIC_MIN_PITCH 65
+#define SONIC_MAX_PITCH 400
+
+/* These are used to down-sample some inputs to improve speed */
+#define SONIC_AMDF_FREQ 4000
+
+struct sonicStreamStruct;
+typedef struct sonicStreamStruct *sonicStream;
+
+/* For all of the following functions, numChannels is multiplied by numSamples
+ to determine the actual number of values read or returned. */
+
+/* Create a sonic stream. Return NULL only if we are out of memory and cannot
+ allocate the stream. Set numChannels to 1 for mono, and 2 for stereo. */
+sonicStream sonicCreateStream(int sampleRate, int numChannels);
+/* Destroy the sonic stream. */
+void sonicDestroyStream(sonicStream stream);
+/* Use this to write floating point data to be speed up or down into the stream.
+ Values must be between -1 and 1. Return 0 if memory realloc failed, otherwise 1 */
+int sonicWriteFloatToStream(sonicStream stream, float *samples, int numSamples);
+/* Use this to write 16-bit data to be speed up or down into the stream.
+ Return 0 if memory realloc failed, otherwise 1 */
+int sonicWriteShortToStream(sonicStream stream, short *samples, int numSamples);
+/* Use this to write 8-bit unsigned data to be speed up or down into the stream.
+ Return 0 if memory realloc failed, otherwise 1 */
+int sonicWriteUnsignedCharToStream(sonicStream stream, unsigned char *samples, int numSamples);
+/* Use this to read floating point data out of the stream. Sometimes no data
+ will be available, and zero is returned, which is not an error condition. */
+int sonicReadFloatFromStream(sonicStream stream, float *samples, int maxSamples);
+/* Use this to read 16-bit data out of the stream. Sometimes no data will
+ be available, and zero is returned, which is not an error condition. */
+int sonicReadShortFromStream(sonicStream stream, short *samples, int maxSamples);
+/* Use this to read 8-bit unsigned data out of the stream. Sometimes no data will
+ be available, and zero is returned, which is not an error condition. */
+int sonicReadUnsignedCharFromStream(sonicStream stream, unsigned char *samples, int maxSamples);
+/* Force the sonic stream to generate output using whatever data it currently
+ has. No extra delay will be added to the output, but flushing in the middle of
+ words could introduce distortion. */
+int sonicFlushStream(sonicStream stream);
+/* Return the number of samples in the output buffer */
+int sonicSamplesAvailable(sonicStream stream);
+/* Get the speed of the stream. */
+float sonicGetSpeed(sonicStream stream);
+/* Set the speed of the stream. */
+void sonicSetSpeed(sonicStream stream, float speed);
+/* Get the pitch of the stream. */
+float sonicGetPitch(sonicStream stream);
+/* Set the pitch of the stream. */
+void sonicSetPitch(sonicStream stream, float pitch);
+/* Get the scaling factor of the stream. */
+float sonicGetVolume(sonicStream stream);
+/* Set the scaling factor of the stream. */
+void sonicSetVolume(sonicStream stream, float volume);
+/* Get the sample rate of the stream. */
+int sonicGetSampleRate(sonicStream stream);
+/* Get the number of channels. */
+int sonicGetNumChannels(sonicStream stream);
+/* This is a non-stream oriented interface to just change the speed of a sound
+ sample. It works in-place on the sample array, so there must be at least
+ speed*numSamples available space in the array. Returns the new number of samples. */
+int sonicChangeFloatSpeed(float *samples, int numSamples, float speed, float pitch,
+ float volume, int sampleRate, int numChannels);
+/* This is a non-stream oriented interface to just change the speed of a sound
+ sample. It works in-place on the sample array, so there must be at least
+ speed*numSamples available space in the array. Returns the new number of samples. */
+int sonicChangeShortSpeed(short *samples, int numSamples, float speed, float pitch,
+ float volume, int sampleRate, int numChannels);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/navit/support/espeak/speak.c b/navit/support/espeak/speak.c
index ab33a867b..afd9dfafd 100755..100644
--- a/navit/support/espeak/speak.c
+++ b/navit/support/espeak/speak.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2013 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -24,9 +24,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
#ifndef PLATFORM_DOS
#ifdef PLATFORM_WINDOWS
+#include <fcntl.h>
+#include <io.h>
#include <windows.h>
#include <winreg.h>
#else
@@ -36,6 +39,7 @@
#ifndef NEED_GETOPT
#include <getopt.h>
+#include <stdbool.h> // for "true"
#endif
#include <time.h>
#include <signal.h>
@@ -69,48 +73,52 @@ static const char *help_text =
"\nspeak [options] [\"<words>\"]\n\n"
"-f <text file> Text file to speak\n"
"--stdin Read text input from stdin instead of a file\n\n"
-"If neither -f nor --stdin, <words> are spoken, or if none then text is\n"
-"spoken from stdin, each line separately.\n\n"
+"If neither -f nor --stdin, then <words> are spoken, or if none then text\n"
+"is spoken from stdin, each line separately.\n\n"
"-a <integer>\n"
"\t Amplitude, 0 to 200, default is 100\n"
"-g <integer>\n"
"\t Word gap. Pause between words, units of 10mS at the default speed\n"
+"-k <integer>\n"
+"\t Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
+"\t higher values indicate a pitch increase (try -k20).\n"
"-l <integer>\n"
"\t Line length. If not zero (which is the default), consider\n"
"\t lines less than this length as end-of-clause\n"
"-p <integer>\n"
"\t Pitch adjustment, 0 to 99, default is 50\n"
"-s <integer>\n"
-"\t Speed in words per minute 80 to 390, default is 170\n"
+"\t Speed in words per minute, 80 to 450, default is 175\n"
"-v <voice name>\n"
"\t Use voice file of this name from espeak-data/voices\n"
"-w <wave file name>\n"
-"\t Write output to this WAV file, rather than speaking it directly\n"
+"\t Write speech to this WAV file, rather than speaking it directly\n"
"-b\t Input text encoding, 1=UTF8, 2=8 bit, 4=16 bit \n"
"-m\t Interpret SSML markup, and ignore other < > tags\n"
"-q\t Quiet, don't produce any speech (may be useful with -x)\n"
"-x\t Write phoneme mnemonics to stdout\n"
"-X\t Write phonemes mnemonics and translation trace to stdout\n"
"-z\t No final sentence pause at the end of the text\n"
-"--stdout Write speech output to stdout\n"
"--compile=<voice name>\n"
-"\t Compile the pronunciation rules and dictionary in the current\n"
-"\t directory. =<voice name> is optional and specifies which language\n"
+"\t Compile pronunciation rules and dictionary from the current\n"
+"\t directory. <voice name> specifies the language\n"
+"--ipa Write phonemes to stdout using International Phonetic Alphabet\n"
+"\t --ipa=1 Use ties, --ipa=2 Use ZWJ, --ipa=3 Separate with _\n"
"--path=\"<path>\"\n"
"\t Specifies the directory containing the espeak-data directory\n"
+"--pho Write mbrola phoneme data (.pho) to stdout or to the file in --phonout\n"
"--phonout=\"<filename>\"\n"
-"\t Write output from -x -X commands and mbrola phoneme data to this file\n"
+"\t Write phoneme output from -x -X --ipa and --pho to this file\n"
"--punct=\"<characters>\"\n"
"\t Speak the names of punctuation characters during speaking. If\n"
"\t =<characters> is omitted, all punctuation is spoken.\n"
"--split=\"<minutes>\"\n"
"\t Starts a new WAV file every <minutes>. Used with -w\n"
+"--stdout Write speech output to stdout\n"
+"--version Shows version number and date, and location of espeak-data\n"
"--voices=<language>\n"
"\t List the available voices for the specified language.\n"
-"\t If <language> is omitted, then list all voices.\n"
-"-k <integer>\n"
-"\t Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
-"\t higher values = a pitch increase (try -k20).\n";
+"\t If <language> is omitted, then list all voices.\n";
void DisplayVoices(FILE *f_out, char *language);
@@ -155,14 +163,16 @@ void DisplayVoices(FILE *f_out, char *language)
const char *p;
int len;
int count;
- int scores = 0;
+ int c;
+ int j;
const espeak_VOICE *v;
const char *lang_name;
char age_buf[12];
+ char buf[80];
const espeak_VOICE **voices;
espeak_VOICE voice_select;
- static char genders[4] = {' ','M','F',' '};
+ static char genders[4] = {'-','M','F','-'};
if((language != NULL) && (language[0] != 0))
{
@@ -172,14 +182,13 @@ void DisplayVoices(FILE *f_out, char *language)
voice_select.gender = 0;
voice_select.name = NULL;
voices = espeak_ListVoices(&voice_select);
- scores = 1;
}
else
{
voices = espeak_ListVoices(NULL);
}
- fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Langs\n");
+ fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Languages\n");
for(ix=0; (v = voices[ix]) != NULL; ix++)
{
@@ -197,8 +206,16 @@ void DisplayVoices(FILE *f_out, char *language)
if(count==0)
{
- fprintf(f_out,"%2d %-12s%s%c %-17s %-11s ",
- p[0],lang_name,age_buf,genders[v->gender],v->name,v->identifier);
+ for(j=0; j < sizeof(buf); j++)
+ {
+ // replace spaces in the name
+ if((c = v->name[j]) == ' ')
+ c = '_';
+ if((buf[j] = c) == 0)
+ break;
+ }
+ fprintf(f_out,"%2d %-12s%s%c %-20s %-13s ",
+ p[0],lang_name,age_buf,genders[v->gender],buf,v->identifier);
}
else
{
@@ -207,14 +224,14 @@ void DisplayVoices(FILE *f_out, char *language)
count++;
p += len+2;
}
-// if(scores)
-// fprintf(f_out,"%3d ",v->score);
fputc('\n',f_out);
}
} // end of DisplayVoices
-
+void WVoiceChanged(voice_t *wvoice)
+{
+}
static int OpenWaveFile(const char *path, int rate)
//=================================================
@@ -229,10 +246,22 @@ static int OpenWaveFile(const char *path, int rate)
if(path == NULL)
return(2);
- if(strcmp(path,"stdout")==0)
- f_wave = stdout;
- else
- f_wave = fopen(path,"wb");
+ while(isspace(*path)) path++;
+
+ f_wave = NULL;
+ if(path[0] != 0)
+ {
+ if(strcmp(path,"stdout")==0)
+ {
+#ifdef PLATFORM_WINDOWS
+// prevent Windows adding 0x0d before 0x0a bytes
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
+ f_wave = stdout;
+ }
+ else
+ f_wave = fopen(path,"wb");
+ }
if(f_wave != NULL)
{
@@ -274,7 +303,7 @@ static void CloseWaveFile()
-void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
+void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr)
{//======================================================================================
// Do nothing in the command-line version.
if(type == 2)
@@ -285,7 +314,7 @@ void MarkerEvent(int type, unsigned int char_position, int value, unsigned char
static int WavegenFile(void)
{//=========================
int finished;
- unsigned char wav_outbuf[512];
+ unsigned char wav_outbuf[1024];
char fname[210];
out_ptr = out_start = wav_outbuf;
@@ -340,14 +369,12 @@ static void init_path(char *argv0, char *path_specified)
char *env;
unsigned char buf[sizeof(path_home)-12];
-#if 0
if(((env = getenv("ESPEAK_DATA_PATH")) != NULL) && ((strlen(env)+12) < sizeof(path_home)))
{
sprintf(path_home,"%s\\espeak-data",env);
if(GetFileLength(path_home) == -2)
return; // an espeak-data directory exists in the directory specified by environment variable
}
-#endif
strcpy(path_home,argv0);
if((p = strrchr(path_home,'\\')) != NULL)
@@ -370,16 +397,13 @@ static void init_path(char *argv0, char *path_specified)
strcpy(path_home,PATH_ESPEAK_DATA);
#else
char *env;
-#if 0
if((env = getenv("ESPEAK_DATA_PATH")) != NULL)
{
snprintf(path_home,sizeof(path_home),"%s/espeak-data",env);
if(GetFileLength(path_home) == -2)
return; // an espeak-data directory exists
}
-#endif
-#if 0
snprintf(path_home,sizeof(path_home),"%s/espeak-data",getenv("HOME"));
if(access(path_home,R_OK) != 0)
{
@@ -387,7 +411,6 @@ static void init_path(char *argv0, char *path_specified)
}
#endif
#endif
-#endif
}
@@ -395,6 +418,7 @@ static int initialise(void)
{//========================
int param;
int result;
+ int srate = 22050; // default sample rate
// It seems that the wctype functions don't work until the locale has been set
// to something other than the default "C". Then, not only Latin1 but also the
@@ -402,18 +426,15 @@ static int initialise(void)
#ifdef PLATFORM_RISCOS
setlocale(LC_CTYPE,"ISO8859-1");
#else
-#if 0
if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
{
if(setlocale(LC_CTYPE,"UTF-8") == NULL)
setlocale(LC_CTYPE,"");
}
#endif
-#endif
- WavegenInit(22050,0); // 22050
- if((result = LoadPhData()) != 1)
+ if((result = LoadPhData(&srate)) != 1)
{
if(result == -1)
{
@@ -423,8 +444,9 @@ static int initialise(void)
else
fprintf(stderr,"Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n",result,version_phdata,path_home);
}
+ WavegenInit(srate,0);
LoadConfig();
- SetVoiceStack(NULL);
+ SetVoiceStack(NULL, "");
SynthesizeInit();
for(param=0; param<N_SPEECH_PARAM; param++)
@@ -436,14 +458,16 @@ static int initialise(void)
static void StopSpeak(int unused)
{//==============================
-// signal(SIGINT,SIG_IGN);
+ signal(SIGINT,SIG_IGN);
// DEBUG
// printf("\n*** Interrupting speech output (use Ctrl-D to actually quit).\n");
fflush(stdout);
SpeakNextClause(NULL,NULL,5);
-// signal(SIGINT,StopSpeak);
+ signal(SIGINT,StopSpeak);
} // end of StopSpeak()
+
+
#ifdef NEED_GETOPT
struct option {
char *name;
@@ -480,7 +504,10 @@ int main (int argc, char **argv)
{"stdout", no_argument, 0, 0x105},
{"split", optional_argument, 0, 0x106},
{"path", required_argument, 0, 0x107},
- {"phonout", required_argument, 0, 0x108},
+ {"phonout", required_argument, 0, 0x108},
+ {"pho", no_argument, 0, 0x109},
+ {"ipa", optional_argument, 0, 0x10a},
+ {"version", no_argument, 0, 0x10b},
{0, 0, 0, 0}
};
@@ -493,7 +520,7 @@ int main (int argc, char **argv)
int option_index = 0;
int c;
int value;
- int speed=170;
+ int speed=175;
int ix;
char *optarg2;
int amp = 100; // default
@@ -505,11 +532,9 @@ int main (int argc, char **argv)
espeak_VOICE voice_select;
char filename[200];
char voicename[40];
- char dictname[40];
voicename[0] = 0;
mbrola_name[0] = 0;
- dictname[0] = 0;
wavefile[0] = 0;
filename[0] = 0;
option_linelength = 0;
@@ -544,6 +569,9 @@ int main (int argc, char **argv)
if(c == '-')
{
+ if(p[0] == 0)
+ break; // -- means don't interpret further - as commands
+
opt_string="";
for(ix=0; ;ix++)
{
@@ -574,7 +602,7 @@ int main (int argc, char **argv)
}
}
#else
- while(1)
+ while(true)
{
c = getopt_long (argc, argv, "a:b:f:g:hk:l:p:qs:v:w:xXmz", // NOTE: also change arg_opts to indicate which commands have a numeric value
long_options, &option_index);
@@ -595,9 +623,9 @@ int main (int argc, char **argv)
break;
case 'h':
- printf("\nspeak text-to-speech: %s\n%s",version_string,help_text);
+ init_path(argv[0],data_path);
+ printf("\nspeak text-to-speech: %s Data at: %s\n%s",version_string,path_home,help_text);
exit(0);
- break;
case 'k':
option_capitals = atoi(optarg2);
@@ -710,6 +738,30 @@ int main (int argc, char **argv)
}
break;
+ case 0x109: // --pho
+ option_mbrola_phonemes = 16;
+ break;
+
+ case 0x10a: // --ipa
+ option_phonemes = 3;
+ if(optarg2 != NULL)
+ {
+ value = -1;
+ sscanf(optarg2,"%d",&value);
+ if((value<0) || (value>3))
+ {
+ fprintf(stderr,"Bad value for -ipa=\n");
+ value = 0;
+ }
+ option_phonemes += value;
+ }
+ break;
+
+ case 0x10b: // --version
+ init_path(argv[0],data_path);
+ printf("speak text-to-speech: %s Data at: %s\n",version_string,path_home);
+ exit(0);
+
default:
exit(0);
}
@@ -718,11 +770,22 @@ int main (int argc, char **argv)
init_path(argv[0],data_path);
initialise();
+ if(voicename[0] == 0)
+ strcpy(voicename,"default");
- if(flag_compile)
+ if(SetVoiceByName(voicename) != EE_OK)
{
- LoadVoice(voicename,5);
+ memset(&voice_select,0,sizeof(voice_select));
+ voice_select.languages = voicename;
+ if(SetVoiceByProperties(&voice_select) != EE_OK)
+ {
+ fprintf(stderr,"%svoice '%s'\n",err_load,voicename);
+ exit(2);
+ }
+ }
+ if(flag_compile)
+ {
#ifdef PLATFORM_DOS
char path_dsource[sizeof(path_home)+20];
strcpy(path_dsource,path_home);
@@ -744,20 +807,6 @@ int main (int argc, char **argv)
}
- if(voicename[0] == 0)
- strcpy(voicename,"default");
-
- if(SetVoiceByName(voicename) != EE_OK)
- {
- memset(&voice_select,0,sizeof(voice_select));
- voice_select.languages = voicename;
- if(SetVoiceByProperties(&voice_select) != EE_OK)
- {
- fprintf(stderr,"%svoice '%s'\n",err_load,voicename);
- exit(2);
- }
- }
-
SetParameter(espeakRATE,speed,0);
SetParameter(espeakVOLUME,amp,0);
SetParameter(espeakCAPITALS,option_capitals,0);
diff --git a/navit/support/espeak/speak_init.c b/navit/support/espeak/speak_init.c
deleted file mode 100644
index 06a34235f..000000000
--- a/navit/support/espeak/speak_init.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "../../plugin.h"
-
-void
-plugin_init(void)
-{
-}
diff --git a/navit/support/espeak/speak_lib.c b/navit/support/espeak/speak_lib.c
index 85c1b39a3..99269c42f 100644
--- a/navit/support/espeak/speak_lib.c
+++ b/navit/support/espeak/speak_lib.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2013 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -32,8 +32,11 @@
#include <sys/stat.h>
#ifdef PLATFORM_WINDOWS
+#include <fcntl.h>
+#include <io.h>
#include <windows.h>
-#else
+#include <winreg.h>
+#else /* PLATFORM_POSIX */
#include <unistd.h>
#endif
@@ -48,8 +51,9 @@
#include "event.h"
#include "wave.h"
+#define double(x) ((double)(x))
+
unsigned char *outbuf=NULL;
-extern espeak_VOICE voice_selected;
espeak_EVENT *event_list=NULL;
int event_list_ix=0;
@@ -61,11 +65,23 @@ static unsigned int my_unique_identifier=0;
static void* my_user_data=NULL;
static espeak_AUDIO_OUTPUT my_mode=AUDIO_OUTPUT_SYNCHRONOUS;
static int synchronous_mode = 1;
+static int out_samplerate = 0;
+static int voice_samplerate = 22050;
+static espeak_ERROR err = EE_OK;
+
t_espeak_callback* synth_callback = NULL;
int (* uri_callback)(int, const char *, const char *) = NULL;
int (* phoneme_callback)(const char *) = NULL;
char path_home[N_PATH_HOME]; // this is the espeak-data directory
+extern int saved_parameters[N_SPEECH_PARAM]; //Parameters saved on synthesis start
+
+
+void WVoiceChanged(voice_t *wvoice)
+{//=================================
+// Voice change in wavegen
+ voice_samplerate = wvoice->samplerate;
+}
#ifdef USE_ASYNC
@@ -73,12 +89,12 @@ char path_home[N_PATH_HOME]; // this is the espeak-data directory
static int dispatch_audio(short* outbuf, int length, espeak_EVENT* event)
{//======================================================================
ENTER("dispatch_audio");
-
+
int a_wave_can_be_played = fifo_is_command_enabled();
-
+
#ifdef DEBUG_ENABLED
- SHOW("*** dispatch_audio > uid=%d, [write=%p (%d bytes)], sample=%d, a_wave_can_be_played = %d\n",
- (event) ? event->unique_identifier : 0, wave_test_get_write_buffer(), 2*length,
+ SHOW("*** dispatch_audio > uid=%d, [write=%p (%d bytes)], sample=%d, a_wave_can_be_played = %d\n",
+ (event) ? event->unique_identifier : 0, wave_test_get_write_buffer(), 2*length,
(event) ? event->sample : 0,
a_wave_can_be_played);
#endif
@@ -87,6 +103,36 @@ static int dispatch_audio(short* outbuf, int length, espeak_EVENT* event)
{
case AUDIO_OUTPUT_PLAYBACK:
{
+ int event_type=0;
+ if(event)
+ {
+ event_type = event->type;
+ }
+
+ if(event_type == espeakEVENT_SAMPLERATE)
+ {
+ voice_samplerate = event->id.number;
+
+ if(out_samplerate != voice_samplerate)
+ {
+ if(out_samplerate != 0)
+ {
+ // sound was previously open with a different sample rate
+ wave_close(my_audio);
+ sleep(1);
+ }
+ out_samplerate = voice_samplerate;
+ if(!wave_init(voice_samplerate))
+ {
+ err = EE_INTERNAL_ERROR;
+ return(-1);
+ }
+ wave_set_callback_is_output_enabled( fifo_is_command_enabled);
+ my_audio = wave_open("alsa");
+ event_init();
+ }
+ }
+
if (outbuf && length && a_wave_can_be_played)
{
wave_write (my_audio, (char*)outbuf, 2*length);
@@ -95,8 +141,8 @@ static int dispatch_audio(short* outbuf, int length, espeak_EVENT* event)
while(a_wave_can_be_played) {
// TBD: some event are filtered here but some insight might be given
// TBD: in synthesise.cpp for avoiding to create WORDs with size=0.
- // TBD: For example sentence "or ALT)." returns three words
- // "or", "ALT" and "".
+ // TBD: For example sentence "or ALT)." returns three words
+ // "or", "ALT" and "".
// TBD: the last one has its size=0.
if (event && (event->type == espeakEVENT_WORD) && (event->length==0))
{
@@ -133,7 +179,7 @@ static int dispatch_audio(short* outbuf, int length, espeak_EVENT* event)
SHOW_TIME("LEAVE dispatch_audio\n");
- return (a_wave_can_be_played==0); // 1 = stop synthesis
+ return (a_wave_can_be_played==0); // 1 = stop synthesis, -1 = error
}
@@ -144,12 +190,12 @@ static int create_events(short* outbuf, int length, espeak_EVENT* event, uint32_
int i=0;
// The audio data are written to the output device.
- // The list of events in event_list (index: event_list_ix) is read:
+ // The list of events in event_list (index: event_list_ix) is read:
// Each event is declared to the "event" object which stores them internally.
// The event object is responsible of calling the external callback
// as soon as the relevant audio sample is played.
- do
+ do
{ // for each event
espeak_EVENT* event;
if (event_list_ix == 0)
@@ -175,7 +221,7 @@ static int create_events(short* outbuf, int length, espeak_EVENT* event, uint32_
}
-int sync_espeak_terminated_msg( uint unique_identifier, void* user_data)
+int sync_espeak_terminated_msg( uint32_t unique_identifier, void* user_data)
{//=====================================================================
ENTER("sync_espeak_terminated_msg");
@@ -192,7 +238,7 @@ int sync_espeak_terminated_msg( uint unique_identifier, void* user_data)
if (my_mode==AUDIO_OUTPUT_PLAYBACK)
{
- while(1)
+ while(1)
{
espeak_ERROR a_error = event_declare(event_list);
if (a_error != EE_BUFFER_FULL)
@@ -222,17 +268,13 @@ static void select_output(espeak_AUDIO_OUTPUT output_type)
my_audio = NULL;
synchronous_mode = 1;
option_waveout = 1; // inhibit portaudio callback from wavegen.cpp
+ out_samplerate = 0;
switch(my_mode)
{
case AUDIO_OUTPUT_PLAYBACK:
+ // wave_init() is now called just before the first wave_write()
synchronous_mode = 0;
-#ifdef USE_ASYNC
- wave_init();
- wave_set_callback_is_output_enabled( fifo_is_command_enabled);
- my_audio = wave_open("alsa");
- event_init();
-#endif
break;
case AUDIO_OUTPUT_RETRIEVAL:
@@ -255,14 +297,14 @@ static void select_output(espeak_AUDIO_OUTPUT output_type)
int GetFileLength(const char *filename)
{//====================================
struct stat statbuf;
-
+
if(stat(filename,&statbuf) != 0)
return(0);
-
+
if((statbuf.st_mode & S_IFMT) == S_IFDIR)
// if(S_ISDIR(statbuf.st_mode))
return(-2); // a directory
-
+
return(statbuf.st_size);
} // end of GetFileLength
@@ -302,7 +344,7 @@ static void init_path(const char *path)
{
sprintf(path_home,"%s/espeak-data",env);
if(GetFileLength(path_home) == -2)
- return; // an espeak-data directory exists
+ return; // an espeak-data directory exists
}
buf[0] = 0;
@@ -327,7 +369,7 @@ static void init_path(const char *path)
{
snprintf(path_home,sizeof(path_home),"%s/espeak-data",env);
if(GetFileLength(path_home) == -2)
- return; // an espeak-data directory exists
+ return; // an espeak-data directory exists
}
snprintf(path_home,sizeof(path_home),"%s/espeak-data",getenv("HOME"));
@@ -338,26 +380,32 @@ static void init_path(const char *path)
#endif
}
-static int initialise(void)
-{//========================
+static int initialise(int control)
+{//===============================
int param;
int result;
+ int srate = 22050; // default sample rate 22050 Hz
+ err = EE_OK;
LoadConfig();
- WavegenInit(22050,0); // 22050
- if((result = LoadPhData()) != 1)
+
+ if((result = LoadPhData(&srate)) != 1) // reads sample rate from espeak-data/phontab
{
if(result == -1)
{
fprintf(stderr,"Failed to load espeak-data\n");
- exit(1);
+ if((control & espeakINITIALIZE_DONT_EXIT) == 0)
+ {
+ exit(1);
+ }
}
else
fprintf(stderr,"Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n",result,version_phdata,path_home);
}
+ WavegenInit(srate,0);
- memset(&voice_selected,0,sizeof(voice_selected));
- SetVoiceStack(NULL);
+ memset(&current_voice_selected,0,sizeof(current_voice_selected));
+ SetVoiceStack(NULL, "");
SynthesizeInit();
InitNamedata();
@@ -454,6 +502,8 @@ static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text,
{
#ifdef USE_ASYNC
finished = create_events((short *)outbuf, length, event_list, a_write_pos);
+ if(finished < 0)
+ return EE_INTERNAL_ERROR;
length = 0; // the wave data are played once.
#endif
}
@@ -483,7 +533,8 @@ static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text,
#ifdef USE_ASYNC
if (my_mode==AUDIO_OUTPUT_PLAYBACK)
{
- dispatch_audio(NULL, 0, NULL); // TBD: test case
+ if(dispatch_audio(NULL, 0, NULL) < 0) // TBD: test case
+ return err = EE_INTERNAL_ERROR;
}
else
{
@@ -496,8 +547,8 @@ static espeak_ERROR Synthesize(unsigned int unique_identifier, const void *text,
}
}
}
- }
- return(EE_OK);
+ }
+ return(EE_OK);
} // end of Synthesize
#ifdef DEBUG_ENABLED
@@ -507,31 +558,35 @@ static const char* label[] = {
"SENTENCE",
"MARK",
"PLAY",
- "END"};
+ "END",
+ "MSG_TERMINATED",
+ "PHONEME",
+ "SAMPLERATE",
+ "??" };
#endif
-void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
-{//======================================================================================
- // type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end
+void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr)
+{//==================================================================================================
+ // type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end, 7=phoneme
ENTER("MarkerEvent");
espeak_EVENT *ep;
double time;
-
+
if((event_list == NULL) || (event_list_ix >= (n_event_list-2)))
return;
-
+
ep = &event_list[event_list_ix++];
ep->type = (espeak_EVENT_TYPE)type;
ep->unique_identifier = my_unique_identifier;
ep->user_data = my_user_data;
ep->text_position = char_position & 0xffffff;
ep->length = char_position >> 24;
-
- time = ((double)(count_samples + mbrola_delay + (out_ptr - out_start)/2)*1000.0)/samplerate;
- ep->audio_position = (int)(time);
+
+ time = (double(count_samples + mbrola_delay + (out_ptr - out_start)/2)*1000.0)/samplerate;
+ ep->audio_position = ((int)(time));
ep->sample = (count_samples + mbrola_delay + (out_ptr - out_start)/2);
-
+
#ifdef DEBUG_ENABLED
SHOW("MarkerEvent > count_samples=%d, out_ptr=%x, out_start=0x%x\n",count_samples, out_ptr, out_start);
SHOW("*** MarkerEvent > type=%s, uid=%d, text_pos=%d, length=%d, audio_position=%d, sample=%d\n",
@@ -542,53 +597,69 @@ void MarkerEvent(int type, unsigned int char_position, int value, unsigned char
if((type == espeakEVENT_MARK) || (type == espeakEVENT_PLAY))
ep->id.name = &namedata[value];
else
+//#ifdef deleted
+// temporarily removed, don't introduce until after eSpeak version 1.46.02
+ if(type == espeakEVENT_PHONEME)
+ {
+ int *p;
+ p = (int *)(ep->id.string);
+ p[0] = value;
+ p[1] = value2;
+ }
+ else
+//#endif
+ {
ep->id.number = value;
+ }
} // end of MarkerEvent
-espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
- unsigned int position, espeak_POSITION_TYPE position_type,
+espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
+ unsigned int position, espeak_POSITION_TYPE position_type,
unsigned int end_position, unsigned int flags, void* user_data)
{//===========================================================================
-
+ int i;
#ifdef DEBUG_ENABLED
ENTER("sync_espeak_Synth");
SHOW("sync_espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text);
#endif
espeak_ERROR aStatus;
-
+
InitText(flags);
my_unique_identifier = unique_identifier;
my_user_data = user_data;
-
+
+ for (i=0; i < N_SPEECH_PARAM; i++)
+ saved_parameters[i] = param_stack[0].parameter[i];
+
switch(position_type)
{
case POS_CHARACTER:
skip_characters = position;
break;
-
+
case POS_WORD:
skip_words = position;
break;
-
+
case POS_SENTENCE:
skip_sentences = position;
break;
-
+
}
if(skip_characters || skip_words || skip_sentences)
skipping_text = 1;
-
+
end_character_position = end_position;
-
+
aStatus = Synthesize(unique_identifier, text, flags);
#ifdef USE_ASYNC
wave_flush(my_audio);
#endif
-
+
SHOW_TIME("LEAVE sync_espeak_Synth");
return aStatus;
} // end of sync_espeak_Synth
@@ -596,29 +667,29 @@ espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text,
-espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size,
- const char *index_mark, unsigned int end_position,
+espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size,
+ const char *index_mark, unsigned int end_position,
unsigned int flags, void* user_data)
{//=========================================================================
espeak_ERROR aStatus;
-
+
InitText(flags);
-
+
my_unique_identifier = unique_identifier;
my_user_data = user_data;
-
+
if(index_mark != NULL)
{
strncpy0(skip_marker, index_mark, sizeof(skip_marker));
skipping_text = 1;
}
-
+
end_character_position = end_position;
-
-
+
+
aStatus = Synthesize(unique_identifier, text, flags | espeakSSML);
SHOW_TIME("LEAVE sync_espeak_Synth_Mark");
-
+
return (aStatus);
} // end of sync_espeak_Synth_Mark
@@ -650,7 +721,7 @@ void sync_espeak_Char(wchar_t character)
char buf[80];
my_unique_identifier = 0;
my_user_data = NULL;
-
+
sprintf(buf,"<say-as interpret-as=\"tts:char\">&#%d;</say-as>",character);
Synthesize(0, buf,espeakSSML);
}
@@ -662,9 +733,13 @@ void sync_espeak_SetPunctuationList(const wchar_t *punctlist)
// Set the list of punctuation which are spoken for "some".
my_unique_identifier = 0;
my_user_data = NULL;
-
- wcsncpy(option_punctlist, punctlist, N_PUNCTLIST);
- option_punctlist[N_PUNCTLIST-1] = 0;
+
+ option_punctlist[0] = 0;
+ if(punctlist != NULL)
+ {
+ wcsncpy(option_punctlist, punctlist, N_PUNCTLIST);
+ option_punctlist[N_PUNCTLIST-1] = 0;
+ }
} // end of sync_espeak_SetPunctuationList
@@ -688,11 +763,12 @@ ESPEAK_API void espeak_SetUriCallback(int (* UriCallback)(int, const char*, cons
uri_callback = UriCallback;
}
-
+#if 0
ESPEAK_API void espeak_SetPhonemeCallback(int (* PhonemeCallback)(const char*))
{//===========================================================================
phoneme_callback = PhonemeCallback;
}
+#endif
ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output_type, int buf_length, const char *path, int options)
{//=============================================================================================================
@@ -705,46 +781,55 @@ ENTER("espeak_Initialize");
#ifdef PLATFORM_RISCOS
setlocale(LC_CTYPE,"ISO8859-1");
#else
- if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
+ if(setlocale(LC_CTYPE,"C.UTF-8") == NULL)
{
if(setlocale(LC_CTYPE,"UTF-8") == NULL)
- setlocale(LC_CTYPE,"");
+ if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
+ setlocale(LC_CTYPE,"");
}
#endif
-
+
init_path(path);
- initialise();
+ initialise(options);
select_output(output_type);
-
+
+ if(f_logespeak)
+ {
+ fprintf(f_logespeak,"INIT mode %d options 0x%x\n",output_type,options);
+ }
+
// buflength is in mS, allocate 2 bytes per sample
- if(buf_length == 0)
+ if((buf_length == 0) || (output_type == AUDIO_OUTPUT_PLAYBACK) || (output_type == AUDIO_OUTPUT_SYNCH_PLAYBACK))
buf_length = 200;
+
outbuf_size = (buf_length * samplerate)/500;
outbuf = (unsigned char*)realloc(outbuf,outbuf_size);
if((out_start = outbuf) == NULL)
return(EE_INTERNAL_ERROR);
-
+
// allocate space for event list. Allow 200 events per second.
// Add a constant to allow for very small buf_length
n_event_list = (buf_length*200)/1000 + 20;
if((event_list = (espeak_EVENT *)realloc(event_list,sizeof(espeak_EVENT) * n_event_list)) == NULL)
return(EE_INTERNAL_ERROR);
-
+
option_phonemes = 0;
- option_phoneme_events = (options & 1);
+ option_mbrola_phonemes = 0;
+ option_phoneme_events = (options & (espeakINITIALIZE_PHONEME_EVENTS | espeakINITIALIZE_PHONEME_IPA));
+
+ VoiceReset(0);
+// SetVoiceByName("default");
- SetVoiceByName("default");
-
for(param=0; param<N_SPEECH_PARAM; param++)
- param_stack[0].parameter[param] = param_defaults[param];
-
- SetParameter(espeakRATE,170,0);
+ param_stack[0].parameter[param] = saved_parameters[param] = param_defaults[param];
+
+ SetParameter(espeakRATE,175,0);
SetParameter(espeakVOLUME,100,0);
SetParameter(espeakCAPITALS,option_capitals,0);
SetParameter(espeakPUNCTUATION,option_punctuation,0);
SetParameter(espeakWORDGAP,0,0);
- DoVoiceChange(voice);
-
+// DoVoiceChange(voice);
+
#ifdef USE_ASYNC
fifo_init();
#endif
@@ -754,10 +839,10 @@ ENTER("espeak_Initialize");
-ESPEAK_API espeak_ERROR espeak_Synth(const void *text, size_t size,
- unsigned int position,
+ESPEAK_API espeak_ERROR espeak_Synth(const void *text, size_t size,
+ unsigned int position,
espeak_POSITION_TYPE position_type,
- unsigned int end_position, unsigned int flags,
+ unsigned int end_position, unsigned int flags,
unsigned int* unique_identifier, void* user_data)
{//=====================================================================================
#ifdef DEBUG_ENABLED
@@ -765,6 +850,12 @@ ESPEAK_API espeak_ERROR espeak_Synth(const void *text, size_t size,
SHOW("espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text);
#endif
+ if(f_logespeak)
+ {
+ fprintf(f_logespeak,"\nSYNTH posn %d %d %d flags 0x%x\n%s\n",position,end_position,position_type,flags, (const char *)text);
+ fflush(f_logespeak);
+ }
+
espeak_ERROR a_error=EE_INTERNAL_ERROR;
static unsigned int temp_identifier;
@@ -812,10 +903,10 @@ ESPEAK_API espeak_ERROR espeak_Synth(const void *text, size_t size,
-ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text, size_t size,
- const char *index_mark,
- unsigned int end_position,
- unsigned int flags,
+ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text, size_t size,
+ const char *index_mark,
+ unsigned int end_position,
+ unsigned int flags,
unsigned int* unique_identifier,
void* user_data)
{//=========================================================================
@@ -827,6 +918,12 @@ ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text, size_t size,
espeak_ERROR a_error=EE_OK;
static unsigned int temp_identifier;
+ if(f_logespeak)
+ {
+ fprintf(f_logespeak,"\nSYNTH MARK %s posn %d flags 0x%x\n%s\n",index_mark,end_position,flags, (const char *)text);
+ }
+
+
if (unique_identifier == NULL)
{
unique_identifier = &temp_identifier;
@@ -840,15 +937,15 @@ ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text, size_t size,
#ifdef USE_ASYNC
// Create the mark command
- t_espeak_command* c1 = create_espeak_mark(text, size, index_mark, end_position,
+ t_espeak_command* c1 = create_espeak_mark(text, size, index_mark, end_position,
flags, user_data);
-
+
// Retrieve the unique identifier
*unique_identifier = c1->u.my_mark.unique_identifier;
-
+
// Create the "terminated msg" command (same uid)
t_espeak_command* c2 = create_espeak_terminated_msg(*unique_identifier, user_data);
-
+
// Try to add these 2 commands (single transaction)
if (c1 && c2)
{
@@ -877,6 +974,11 @@ ESPEAK_API espeak_ERROR espeak_Key(const char *key)
ENTER("espeak_Key");
// symbolic name, symbolicname_character - is there a system resource of symbolicnames per language
+ if(f_logespeak)
+ {
+ fprintf(f_logespeak,"\nKEY %s\n",key);
+ }
+
espeak_ERROR a_error = EE_OK;
if(synchronous_mode)
@@ -903,6 +1005,11 @@ ESPEAK_API espeak_ERROR espeak_Char(wchar_t character)
ENTER("espeak_Char");
// is there a system resource of character names per language?
+ if(f_logespeak)
+ {
+ fprintf(f_logespeak,"\nCHAR U+%x\n",character);
+ }
+
#ifdef USE_ASYNC
espeak_ERROR a_error;
@@ -999,6 +1106,10 @@ ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int valu
{//=============================================================================================
ENTER("espeak_SetParameter");
+ if(f_logespeak)
+ {
+ fprintf(f_logespeak,"SETPARAM %d %d %d\n",parameter,value,relative);
+ }
#ifdef USE_ASYNC
espeak_ERROR a_error;
@@ -1055,18 +1166,34 @@ ESPEAK_API void espeak_SetPhonemeTrace(int value, FILE *stream)
{//============================================================
ENTER("espeak_SetPhonemes");
/* Controls the output of phoneme symbols for the text
- value=0 No phoneme output (default)
- value=1 Output the translated phoneme symbols for the text
- value=2 as (1), but also output a trace of how the translation was done (matching rules and list entries)
+ bits 0-3:
+ value=0 No phoneme output (default)
+ value=1 Output the translated phoneme symbols for the text
+ value=2 as (1), but also output a trace of how the translation was done (matching rules and list entries)
+ value=3 as (1), but produces IPA phoneme names rather than ascii
+ bit 4: produce mbrola pho data
*/
- option_phonemes = value;
+ option_phonemes = value & 7;
+ option_mbrola_phonemes = value & 16;
f_trans = stream;
if(stream == NULL)
f_trans = stderr;
-
+
} // end of espeak_SetPhonemes
+ESPEAK_API const char *espeak_TextToPhonemes(const void **textptr, int textmode, int phonememode)
+{//=================================================================================================
+ /* phoneme_mode bits 0-3: 0=only phoneme names, 1=ties, 2=ZWJ, 3=underscore separator
+ bits 4-7: 0=eSpeak phoneme names, 1=IPA
+ */
+
+ option_multibyte = textmode & 7;
+ *textptr = TranslateClause(translator, NULL, *textptr, NULL, NULL);
+ return(GetTranslatedPhonemeString(phonememode));
+}
+
+
ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags)
{//=============================================================================
ENTER("espeak_CompileDictionary");
@@ -1076,6 +1203,7 @@ ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags)
ESPEAK_API espeak_ERROR espeak_Cancel(void)
{//===============================
+ int i;
#ifdef USE_ASYNC
ENTER("espeak_Cancel");
fifo_stop();
@@ -1088,6 +1216,10 @@ ESPEAK_API espeak_ERROR espeak_Cancel(void)
SHOW_TIME("espeak_Cancel > LEAVE");
#endif
embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements
+
+ for (i=0; i < N_SPEECH_PARAM; i++)
+ SetParameter(i, saved_parameters[i], 0);
+
return EE_OK;
} // end of espeak_Cancel
@@ -1108,6 +1240,7 @@ ESPEAK_API int espeak_IsPlaying(void)
ESPEAK_API espeak_ERROR espeak_Synchronize(void)
{//=============================================
+ espeak_ERROR berr = err;
#ifdef USE_ASYNC
SHOW_TIME("espeak_Synchronize > ENTER");
while (espeak_IsPlaying())
@@ -1115,12 +1248,13 @@ ESPEAK_API espeak_ERROR espeak_Synchronize(void)
usleep(20000);
}
#endif
+ err = EE_OK;
SHOW_TIME("espeak_Synchronize > LEAVE");
- return EE_OK;
+ return berr;
} // end of espeak_Synchronize
-
-extern void FreePhData(void);
+#include "synthdata.h"
+#include "voices.h"
ESPEAK_API espeak_ERROR espeak_Terminate(void)
{//===========================================
@@ -1134,6 +1268,7 @@ ESPEAK_API espeak_ERROR espeak_Terminate(void)
{
wave_close(my_audio);
wave_terminate();
+ out_samplerate = 0;
}
#endif
@@ -1142,15 +1277,26 @@ ESPEAK_API espeak_ERROR espeak_Terminate(void)
Free(outbuf);
outbuf = NULL;
FreePhData();
+ FreeVoiceList();
+
+ if(f_logespeak)
+ {
+ fclose(f_logespeak);
+ f_logespeak = NULL;
+ }
return EE_OK;
} // end of espeak_Terminate
-ESPEAK_API const char *espeak_Info(void)
-{//=======================================
+ESPEAK_API const char *espeak_Info(const char **ptr)
+{//=================================================
+ if(ptr != NULL)
+ {
+ *ptr = path_home;
+ }
return(version_string);
}
#pragma GCC visibility pop
-
+
diff --git a/navit/support/espeak/speak_lib.h b/navit/support/espeak/speak_lib.h
index 25c24c173..f33e03136 100644
--- a/navit/support/espeak/speak_lib.h
+++ b/navit/support/espeak/speak_lib.h
@@ -1,7 +1,7 @@
#ifndef SPEAK_LIB_H
#define SPEAK_LIB_H
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2012 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -26,8 +26,15 @@
/*************************************************************/
#include <stdio.h>
-#include <wchar.h>
-#define ESPEAK_API_REVISION 5
+#include <stddef.h>
+
+#ifdef __WIN32__
+#define ESPEAK_API __declspec(dllexport)
+#else
+#define ESPEAK_API
+#endif
+
+#define ESPEAK_API_REVISION 9
/*
Revision 2
Added parameter "options" to eSpeakInitialize()
@@ -40,21 +47,41 @@ Revision 4
Revision 5
Added espeakCHARS_16BIT
+
+Revision 6
+ Added macros: espeakRATE_MINIMUM, espeakRATE_MAXIMUM, espeakRATE_NORMAL
+
+Revision 7 24.Dec.2011
+ Changed espeak_EVENT structure to add id.string[] for phoneme mnemonics.
+ Added espeakINITIALIZE_PHONEME_IPA option for espeak_Initialize() to report phonemes as IPA names.
+
+Revision 8 26.Apr.2013
+ Added function espeak_TextToPhonemes().
+
+Revision 9 30.May.2013
+ Changed function espeak_TextToPhonemes().
+
*/
/********************/
/* Initialization */
/********************/
+// values for 'value' in espeak_SetParameter(espeakRATE, value, 0), nominally in words-per-minute
+#define espeakRATE_MINIMUM 80
+#define espeakRATE_MAXIMUM 450
+#define espeakRATE_NORMAL 175
+
typedef enum {
espeakEVENT_LIST_TERMINATED = 0, // Retrieval mode: terminates the event list.
espeakEVENT_WORD = 1, // Start of word
- espeakEVENT_SENTENCE, // Start of sentence
- espeakEVENT_MARK, // Mark
- espeakEVENT_PLAY, // Audio element
- espeakEVENT_END, // End of sentence or clause
- espeakEVENT_MSG_TERMINATED, // End of message
- espeakEVENT_PHONEME // Phoneme, if enabled in espeak_Initialize()
+ espeakEVENT_SENTENCE = 2, // Start of sentence
+ espeakEVENT_MARK = 3, // Mark
+ espeakEVENT_PLAY = 4, // Audio element
+ espeakEVENT_END = 5, // End of sentence or clause
+ espeakEVENT_MSG_TERMINATED = 6, // End of message
+ espeakEVENT_PHONEME = 7, // Phoneme, if enabled in espeak_Initialize()
+ espeakEVENT_SAMPLERATE = 8 // internal use, set sample rate
} espeak_EVENT_TYPE;
@@ -68,11 +95,12 @@ typedef struct {
int sample; // sample id (internal use)
void* user_data; // pointer supplied by the calling program
union {
- int number; // used for WORD and SENTENCE events. For PHONEME events this is the phoneme mnemonic.
+ int number; // used for WORD and SENTENCE events.
const char *name; // used for MARK and PLAY events. UTF8 string
+ char string[8]; // used for phoneme names (UTF8). Terminated by a zero byte unless the name needs the full 8 bytes.
} id;
} espeak_EVENT;
-/*
+/*
When a message is supplied to espeak_synth, the request is buffered and espeak_synth returns. When the message is really processed, the callback function will be repetedly called.
@@ -80,16 +108,16 @@ typedef struct {
In PLAYBACK mode, the callback function is called as soon as an event happens.
- For example suppose that the following message is supplied to espeak_Synth:
+ For example suppose that the following message is supplied to espeak_Synth:
"hello, hello."
* Once processed in RETRIEVAL mode, it could lead to 3 calls of the callback function :
** Block 1:
- <audio data> +
+ <audio data> +
List of events: SENTENCE + WORD + LIST_TERMINATED
-
+
** Block 2:
<audio data> +
List of events: WORD + END + LIST_TERMINATED
@@ -126,11 +154,11 @@ typedef enum {
typedef enum {
/* PLAYBACK mode: plays the audio data, supplies events to the calling program*/
- AUDIO_OUTPUT_PLAYBACK,
+ AUDIO_OUTPUT_PLAYBACK,
/* RETRIEVAL mode: supplies audio data and events to the calling program */
AUDIO_OUTPUT_RETRIEVAL,
-
+
/* SYNCHRONOUS mode: as RETRIEVAL but doesn't return until synthesis is completed */
AUDIO_OUTPUT_SYNCHRONOUS,
@@ -147,20 +175,26 @@ typedef enum {
EE_NOT_FOUND=2
} espeak_ERROR;
+#define espeakINITIALIZE_PHONEME_EVENTS 0x0001
+#define espeakINITIALIZE_PHONEME_IPA 0x0002
+#define espeakINITIALIZE_DONT_EXIT 0x8000
#ifdef __cplusplus
extern "C"
#endif
-int espeak_Initialize(espeak_AUDIO_OUTPUT output, int buflength, const char *path, int options);
+ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output, int buflength, const char *path, int options);
/* Must be called before any synthesis functions are called.
output: the audio data can either be played by eSpeak or passed back by the SynthCallback function.
buflength: The length in mS of sound buffers passed to the SynthCallback function.
+ Value=0 gives a default of 200mS.
+ This paramater is only used for AUDIO_OUTPUT_RETRIEVAL and AUDIO_OUTPUT_SYNCHRONOUS modes.
path: The directory which contains the espeak-data directory, or NULL for the default location.
- options: bit 0: 1=allow espeakEVENT_PHONEME events.
-
+ options: bit 0: 1=allow espeakEVENT_PHONEME events.
+ bit 1: 1= espeakEVENT_PHONEME events give IPA phoneme names, not eSpeak phoneme names
+ bit 15: 1=don't exit if espeak_data is not found (used for --help)
Returns: sample rate in Hz, or -1 (EE_INTERNAL_ERROR).
*/
@@ -170,10 +204,10 @@ typedef int (t_espeak_callback)(short*, int, espeak_EVENT*);
#ifdef __cplusplus
extern "C"
#endif
-void espeak_SetSynthCallback(t_espeak_callback* SynthCallback);
+ESPEAK_API void espeak_SetSynthCallback(t_espeak_callback* SynthCallback);
/* Must be called before any synthesis functions are called.
This specifies a function in the calling program which is called when a buffer of
- speech sound data has been produced.
+ speech sound data has been produced.
The callback function is of the form:
@@ -198,7 +232,7 @@ int SynthCallback(short *wav, int numsamples, espeak_EVENT *events);
#ifdef __cplusplus
extern "C"
#endif
-void espeak_SetUriCallback(int (*UriCallback)(int, const char*, const char*));
+ESPEAK_API void espeak_SetUriCallback(int (*UriCallback)(int, const char*, const char*));
/* This function may be called before synthesis functions are used, in order to deal with
<audio> tags. It specifies a callback function which is called when an <audio> element is
encountered and allows the calling program to indicate whether the sound file which
@@ -239,7 +273,7 @@ int UriCallback(int type, const char *uri, const char *base);
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_Synth(const void *text,
+ESPEAK_API espeak_ERROR espeak_Synth(const void *text,
size_t size,
unsigned int position,
espeak_POSITION_TYPE position_type,
@@ -262,7 +296,7 @@ espeak_ERROR espeak_Synth(const void *text,
start of the text.
position_type: Determines whether "position" is a number of characters, words, or sentences.
- Values:
+ Values:
end_position: If set, this gives a character position at which speaking will stop. A value
of zero indicates no end position.
@@ -273,6 +307,7 @@ espeak_ERROR espeak_Synth(const void *text,
espeakCHARS_8BIT The 8 bit ISO-8859 character set for the particular language.
espeakCHARS_AUTO 8 bit or UTF8 (this is the default)
espeakCHARS_WCHAR Wide characters (wchar_t)
+ espeakCHARS_16BIT 16 bit characters.
espeakSSML Elements within < > are treated as SSML elements, or if not recognised are ignored.
@@ -281,13 +316,16 @@ espeak_ERROR espeak_Synth(const void *text,
espeakENDPAUSE If set then a sentence pause is added at the end of the text. If not set then
this pause is suppressed.
- unique_identifier: message identifier; helpful for identifying later
- data supplied to the callback.
+ unique_identifier: This must be either NULL, or point to an integer variable to
+ which eSpeak writes a message identifier number.
+ eSpeak includes this number in espeak_EVENT messages which are the result of
+ this call of espeak_Synth().
- user_data: pointer which will be passed to the callback function.
+ user_data: a pointer (or NULL) which will be passed to the callback function in
+ espeak_EVENT messages.
- Return: EE_OK: operation achieved
- EE_BUFFER_FULL: the command can not be buffered;
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
@@ -295,7 +333,7 @@ espeak_ERROR espeak_Synth(const void *text,
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_Synth_Mark(const void *text,
+ESPEAK_API espeak_ERROR espeak_Synth_Mark(const void *text,
size_t size,
const char *index_mark,
unsigned int end_position,
@@ -310,8 +348,8 @@ espeak_ERROR espeak_Synth_Mark(const void *text,
For the other parameters, see espeak_Synth()
- Return: EE_OK: operation achieved
- EE_BUFFER_FULL: the command can not be buffered;
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
@@ -319,13 +357,13 @@ espeak_ERROR espeak_Synth_Mark(const void *text,
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_Key(const char *key_name);
+ESPEAK_API espeak_ERROR espeak_Key(const char *key_name);
/* Speak the name of a keyboard key.
If key_name is a single character, it speaks the name of the character.
Otherwise, it speaks key_name as a text string.
- Return: EE_OK: operation achieved
- EE_BUFFER_FULL: the command can not be buffered;
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
@@ -333,11 +371,11 @@ espeak_ERROR espeak_Key(const char *key_name);
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_Char(wchar_t character);
-/* Speak the name of the given character
+ESPEAK_API espeak_ERROR espeak_Char(wchar_t character);
+/* Speak the name of the given character
- Return: EE_OK: operation achieved
- EE_BUFFER_FULL: the command can not be buffered;
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
@@ -378,22 +416,23 @@ typedef enum {
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative);
+ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative);
/* Sets the value of the specified parameter.
relative=0 Sets the absolute value of the parameter.
relative=1 Sets a relative value of the parameter.
parameter:
- espeakRATE: speaking speed in word per minute.
+ espeakRATE: speaking speed in word per minute. Values 80 to 450.
- espeakVOLUME: volume in range 0-100 0=silence
+ espeakVOLUME: volume in range 0-200 or more.
+ 0=silence, 100=normal full volume, greater values may produce amplitude compression or distortion
espeakPITCH: base pitch, range 0-100. 50=normal
espeakRANGE: pitch range, range 0-100. 0-monotone, 50=normal
espeakPUNCTUATION: which punctuation characters to announce:
- value in espeak_PUNCT_TYPE (none, all, some),
+ value in espeak_PUNCT_TYPE (none, all, some),
see espeak_GetParameter() to specify which characters are announced.
espeakCAPITALS: announce capital letters by:
@@ -405,8 +444,8 @@ espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int rela
espeakWORDGAP: pause between words, units of 10mS (at the default speed)
- Return: EE_OK: operation achieved
- EE_BUFFER_FULL: the command can not be buffered;
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
@@ -414,7 +453,7 @@ espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int rela
#ifdef __cplusplus
extern "C"
#endif
-int espeak_GetParameter(espeak_PARAMETER parameter, int current);
+ESPEAK_API int espeak_GetParameter(espeak_PARAMETER parameter, int current);
/* current=0 Returns the default value of the specified parameter.
current=1 Returns the current value of the specified parameter, as set by SetParameter()
*/
@@ -422,14 +461,14 @@ int espeak_GetParameter(espeak_PARAMETER parameter, int current);
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist);
+ESPEAK_API espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist);
/* Specified a list of punctuation characters whose names are to be spoken when the
value of the Punctuation parameter is set to "some".
punctlist: A list of character codes, terminated by a zero character.
- Return: EE_OK: operation achieved
- EE_BUFFER_FULL: the command can not be buffered;
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
@@ -437,11 +476,12 @@ espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist);
#ifdef __cplusplus
extern "C"
#endif
-void espeak_SetPhonemeTrace(int value, FILE *stream);
+ESPEAK_API void espeak_SetPhonemeTrace(int value, FILE *stream);
/* Controls the output of phoneme symbols for the text
value=0 No phoneme output (default)
value=1 Output the translated phoneme symbols for the text
value=2 as (1), but also output a trace of how the translation was done (matching rules and list entries)
+ value=3 as (1), but produces IPA rather than ascii phoneme names
stream output stream for the phoneme symbols (and trace). If stream=NULL then it uses stdout.
*/
@@ -449,7 +489,38 @@ void espeak_SetPhonemeTrace(int value, FILE *stream);
#ifdef __cplusplus
extern "C"
#endif
-void espeak_CompileDictionary(const char *path, FILE *log, int flags);
+ESPEAK_API const char *espeak_TextToPhonemes(const void **textptr, int textmode, int phonememode);
+/* Translates text into phonemes. Call espeak_SetVoiceByName() first, to select a language.
+
+ It returns a pointer to a character string which contains the phonemes for the text up to
+ end of a sentence, or comma, semicolon, colon, or similar punctuation.
+
+ textptr: The address of a pointer to the input text which is terminated by a zero character.
+ On return, the pointer has been advanced past the text which has been translated, or else set
+ to NULL to indicate that the end of the text has been reached.
+
+ textmode: Type of character codes, one of:
+ espeakCHARS_UTF8 UTF8 encoding
+ espeakCHARS_8BIT The 8 bit ISO-8859 character set for the particular language.
+ espeakCHARS_AUTO 8 bit or UTF8 (this is the default)
+ espeakCHARS_WCHAR Wide characters (wchar_t)
+ espeakCHARS_16BIT 16 bit characters.
+
+ phonememode: bits0-3:
+ 0= just phonemes.
+ 1= include ties (U+361) for phoneme names of more than one letter.
+ 2= include zero-width-joiner for phoneme names of more than one letter.
+ 3= separate phonemes with underscore characters.
+
+ bits 4-7:
+ 0= eSpeak's ascii phoneme names.
+ 1= International Phonetic Alphabet (as UTF-8 characters).
+*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags);
/* Compile pronunciation dictionary for a language which corresponds to the currently
selected voice. The required voice should be selected before calling this function.
@@ -473,7 +544,7 @@ typedef struct {
unsigned char gender; // 0=none 1=male, 2=female,
unsigned char age; // 0=not specified, or age in years
unsigned char variant; // only used when passed as a parameter to espeak_SetVoiceByProperties
- unsigned char xx1; // for internal use
+ unsigned char xx1; // for internal use
int score; // for internal use
void *spare; // for internal use
} espeak_VOICE;
@@ -502,24 +573,24 @@ typedef struct {
#ifdef __cplusplus
extern "C"
#endif
-const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec);
+ESPEAK_API const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec);
/* Reads the voice files from espeak-data/voices and creates an array of espeak_VOICE pointers.
The list is terminated by a NULL pointer
If voice_spec is NULL then all voices are listed.
- If voice spec is give, then only the voices which are compatible with the voice_spec
+ If voice spec is given, then only the voices which are compatible with the voice_spec
are listed, and they are listed in preference order.
*/
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_SetVoiceByName(const char *name);
+ESPEAK_API espeak_ERROR espeak_SetVoiceByName(const char *name);
/* Searches for a voice with a matching "name" field. Language is not considered.
"name" is a UTF8 string.
- Return: EE_OK: operation achieved
- EE_BUFFER_FULL: the command can not be buffered;
+ Return: EE_OK: operation achieved
+ EE_BUFFER_FULL: the command can not be buffered;
you may try after a while to call the function again.
EE_INTERNAL_ERROR.
*/
@@ -527,7 +598,7 @@ espeak_ERROR espeak_SetVoiceByName(const char *name);
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_spec);
+ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_spec);
/* An espeak_VOICE structure is used to pass criteria to select a voice. Any of the following
fields may be set:
@@ -547,7 +618,7 @@ espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_spec);
#ifdef __cplusplus
extern "C"
#endif
-espeak_VOICE *espeak_GetCurrentVoice(void);
+ESPEAK_API espeak_VOICE *espeak_GetCurrentVoice(void);
/* Returns the espeak_VOICE data for the currently selected voice.
This is not affected by temporary voice changes caused by SSML elements such as <voice> and <s>
*/
@@ -555,12 +626,12 @@ espeak_VOICE *espeak_GetCurrentVoice(void);
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_Cancel(void);
+ESPEAK_API espeak_ERROR espeak_Cancel(void);
/* Stop immediately synthesis and audio output of the current text. When this
function returns, the audio output is fully stopped and the synthesizer is ready to
synthesize a new message.
- Return: EE_OK: operation achieved
+ Return: EE_OK: operation achieved
EE_INTERNAL_ERROR.
*/
@@ -568,25 +639,25 @@ espeak_ERROR espeak_Cancel(void);
#ifdef __cplusplus
extern "C"
#endif
-int espeak_IsPlaying(void);
+ESPEAK_API int espeak_IsPlaying(void);
/* Returns 1 if audio is played, 0 otherwise.
*/
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_Synchronize(void);
+ESPEAK_API espeak_ERROR espeak_Synchronize(void);
/* This function returns when all data have been spoken.
- Return: EE_OK: operation achieved
+ Return: EE_OK: operation achieved
EE_INTERNAL_ERROR.
*/
#ifdef __cplusplus
extern "C"
#endif
-espeak_ERROR espeak_Terminate(void);
+ESPEAK_API espeak_ERROR espeak_Terminate(void);
/* last function to be called.
- Return: EE_OK: operation achieved
+ Return: EE_OK: operation achieved
EE_INTERNAL_ERROR.
*/
@@ -594,8 +665,8 @@ espeak_ERROR espeak_Terminate(void);
#ifdef __cplusplus
extern "C"
#endif
-const char *espeak_Info(void);
+ESPEAK_API const char *espeak_Info(const char **path_data);
/* Returns the version number string.
- The parameter is for future use, and should be set to NULL
+ path_data returns the path to espeak_data
*/
#endif
diff --git a/navit/support/espeak/speech.h b/navit/support/espeak/speech.h
index 9673b0c8b..cb3887ec0 100755..100644
--- a/navit/support/espeak/speech.h
+++ b/navit/support/espeak/speech.h
@@ -17,27 +17,39 @@
* <http://www.gnu.org/licenses/>. *
***************************************************************************/
+#ifndef SPEECH_H
+#define SPEECH_H
#include <sys/types.h>
// conditional compilation options
#define INCLUDE_KLATT
+//#define INCLUDE_MBROLA
+#define INCLUDE_SONIC
#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
#define ARCH_BIG
#endif
-/* #define PLATFORM_POSIX */
+#ifdef __QNX__
+#define NEED_GETOPT
+#define NO_VARIADIC_MACROS
+#endif
+
+
+#define PLATFORM_POSIX
#define PATHSEP '/'
// USE_PORTAUDIO or USE_PULSEAUDIO are now defined in the makefile
//#define USE_PORTAUDIO
//#define USE_PULSEAUDIO
#define USE_NANOSLEEP
+#ifndef __cdecl
+#define __cdecl
+#endif
//#define ESPEAK_API extern "C"
#ifdef LIBRARY
#define USE_ASYNC
-//#define USE_MBROLA_LIB
#endif
#ifdef _ESPEAKEDIT
@@ -54,7 +66,7 @@
typedef unsigned short USHORT;
typedef unsigned char UCHAR;
typedef double DOUBLEX;
-
+typedef unsigned long long64; // use this for conversion between pointers and integers
@@ -62,19 +74,13 @@ typedef struct {
const char *mnem;
int value;
} MNEM_TAB;
-int LookupMnem(MNEM_TAB *table, char *string);
+int LookupMnem(MNEM_TAB *table, const char *string);
#ifdef PLATFORM_WINDOWS
-#define N_PATH_HOME 220
-#define ESPEAK_API
-#else
-#define N_PATH_HOME 150
-#ifdef __cplusplus
-#define ESPEAK_API extern "C"
+#define N_PATH_HOME 230
#else
-#define ESPEAK_API
-#endif
+#define N_PATH_HOME 160
#endif
extern char path_home[N_PATH_HOME]; // this is the espeak-data directory
@@ -84,3 +90,4 @@ int GetFileLength(const char *filename);
char *Alloc(int size);
void Free(void *ptr);
+#endif // SPEECH_H
diff --git a/navit/support/espeak/synth_mbrola.c b/navit/support/espeak/synth_mbrola.c
index 1055f549f..3d58c350e 100755..100644
--- a/navit/support/espeak/synth_mbrola.c
+++ b/navit/support/espeak/synth_mbrola.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2013 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -17,6 +17,8 @@
* <http://www.gnu.org/licenses/>. *
***************************************************************************/
+
+
#include "StdAfx.h"
#include <stdio.h>
@@ -33,17 +35,16 @@
#include "translate.h"
#include "voice.h"
-extern int Read4Bytes(FILE *f);
-extern void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range);
+int option_mbrola_phonemes;
-#ifdef USE_MBROLA_LIB
+#ifdef INCLUDE_MBROLA
+#include "wavegen.h"
extern unsigned char *outbuf;
#ifndef PLATFORM_WINDOWS
-#include "mbrolib.h"
-void * mb_handle;
+#include "mbrowrap.h"
#else
#include <windows.h>
@@ -64,7 +65,7 @@ PROCVV reset_MBR;
PROCIV lastError_MBR;
PROCVCI lastErrorStr_MBR;
PROCVI setNoError_MBR;
-PROCVI setFreq_MBR;
+PROCIV getFreq_MBR;
PROCVF setVolumeRatio_MBR;
@@ -75,9 +76,9 @@ HINSTANCE hinstDllMBR = NULL;
BOOL load_MBR()
{
if(hinstDllMBR != NULL)
- return TRUE; // already loaded
+ return TRUE; // already loaded
- if (!(hinstDllMBR=LoadLibraryA("mbrola.dll")))
+ if ((hinstDllMBR=LoadLibraryA("mbrola.dll")) == 0)
return FALSE;
init_MBR =(PROCIC) GetProcAddress(hinstDllMBR,"init_MBR");
write_MBR =(PROCIC) GetProcAddress(hinstDllMBR,"write_MBR");
@@ -103,14 +104,11 @@ void unload_MBR()
}
#endif // windows
-#endif // USE_MBROLA_LIB
static MBROLA_TAB *mbrola_tab = NULL;
static int mbrola_control = 0;
-
-
-
+static int mbr_name_prefix = 0;
espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate)
{//===================================================================================
@@ -124,6 +122,7 @@ espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int
mbrola_name[0] = 0;
mbrola_delay = 0;
+ mbr_name_prefix = 0;
if(mbrola_voice == NULL)
{
@@ -133,37 +132,50 @@ espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int
}
sprintf(path,"%s/mbrola/%s",path_home,mbrola_voice);
-#ifdef USE_MBROLA_LIB
+#ifdef PLATFORM_POSIX
+ // if not found, then also look in
+ // usr/share/mbrola/xx, /usr/share/mbrola/xx/xx, /usr/share/mbrola/voices/xx
+ if(GetFileLength(path) <= 0)
+ {
+ sprintf(path,"/usr/share/mbrola/%s",mbrola_voice);
+
+ if(GetFileLength(path) <= 0)
+ {
+ sprintf(path,"/usr/share/mbrola/%s/%s",mbrola_voice,mbrola_voice);
+
+ if(GetFileLength(path) <= 0)
+ {
+ sprintf(path,"/usr/share/mbrola/voices/%s",mbrola_voice);
+ }
+ }
+ }
+ close_MBR();
+#endif
#ifdef PLATFORM_WINDOWS
if(load_MBR() == FALSE) // load mbrola.dll
- return(EE_INTERNAL_ERROR);
+ {
+ fprintf(stderr, "Can't load mbrola.dll\n");
+ return(EE_INTERNAL_ERROR);
+ }
+#endif
if(init_MBR(path) != 0) // initialise the required mbrola voice
return(EE_NOT_FOUND);
setNoError_MBR(1); // don't stop on phoneme errors
-#else
- mb_handle = mbrolib_init(srate);
- mbrolib_parameter m_parameters;
-
- if(mb_handle == NULL)
- return(EE_INTERNAL_ERROR);
-
- MBROLIB_ERROR a_status = mbrolib_set_voice(mb_handle, mbrola_voice);
- if(a_status != MBROLIB_OK)
- return(EE_NOT_FOUND);
-#endif // not windows
-#endif // USE_MBROLA_LIB
// read eSpeak's mbrola phoneme translation data, eg. en1_phtrans
sprintf(path,"%s/mbrola_ph/%s",path_home,phtrans);
size = GetFileLength(path);
- if((f_in = fopen(path,"r")) == NULL)
+ if((f_in = fopen(path,"rb")) == NULL) {
+ close_MBR();
return(EE_NOT_FOUND);
+ }
if((mbrola_tab = (MBROLA_TAB *)realloc(mbrola_tab,size)) == NULL)
{
fclose(f_in);
+ close_MBR();
return(EE_INTERNAL_ERROR);
}
@@ -173,29 +185,19 @@ espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int
{
*pw++ = Read4Bytes(f_in);
}
- fread(mbrola_tab,size,1,f_in);
+ size = fread(mbrola_tab,1,size,f_in);
fclose(f_in);
-
-#ifdef USE_MBROLA_LIB
-#ifdef PLATFORM_WINDOWS
setVolumeRatio_MBR((float)(mbrola_control & 0xff) /16.0f);
-#else
- mbrolib_get_parameter(mb_handle,&m_parameters);
- m_parameters.ignore_error = 1;
- m_parameters.volume_ratio = (float)(mbrola_control & 0xff) /16.0;
- mbrolib_set_parameter(mb_handle,&m_parameters);
-#endif // not windows
-#endif // USE_MBROLA_LIB
-
- option_quiet = 1;
+// srate = getFreq_MBR();
samplerate = srate;
if(srate == 22050)
SetParameter(espeakVOICETYPE,0,0);
else
SetParameter(espeakVOICETYPE,1,0);
strcpy(mbrola_name,mbrola_voice);
- mbrola_delay = 3800; // improve synchronization of events
+// mbrola_delay = 3800; // improve synchronization of events
+ mbrola_delay = 1000; // improve synchronization of events
return(EE_OK);
} // end of LoadMbrolaTable
@@ -204,16 +206,24 @@ static int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev
{//==========================================================================================================================================
// Look up a phoneme in the mbrola phoneme name translation table
// It may give none, 1, or 2 mbrola phonemes
- int mnem = ph->mnemonic;
MBROLA_TAB *pr;
PHONEME_TAB *other_ph;
int found = 0;
+ static int mnem;
// control
// bit 0 skip the next phoneme
// bit 1 match this and Previous phoneme
// bit 2 only at the start of a word
// bit 3 don't match two phonemes across a word boundary
+ // bit 4 add this phoneme name as a prefix to the next phoneme name (used for de4 phoneme prefix '?')
+ // bit 5 only in stressed syllable
+ // bit 6 only at the end of a word
+
+ *name2=0;
+ *split=0;
+ *control=0;
+ mnem = ph->mnemonic;
pr = mbrola_tab;
while(pr->name != 0)
@@ -248,20 +258,36 @@ static int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev
if((pr->control & 4) && (plist->newword == 0)) // only at start of word
found = 0;
+ if((pr->control & 0x40) && (plist[1].newword == 0)) // only at the end of a word
+ found = 0;
+
+ if((pr->control & 0x20) && (plist->stresslevel < plist->wordstress))
+ found = 0; // only in stressed syllables
+
if(found)
{
*name2 = pr->mbr_name2;
*split = pr->percent;
*control = pr->control;
- return(pr->mbr_name);
+
+ if(pr->control & 0x10)
+ {
+ mbr_name_prefix = pr->mbr_name;
+ return(0);
+ }
+ mnem = pr->mbr_name;
+ break;
}
}
pr++;
}
- *name2=0;
- *split=0;
- *control=0;
+
+ if(mbr_name_prefix != 0)
+ {
+ mnem = (mnem << 8) | (mbr_name_prefix & 0xff);
+ }
+ mbr_name_prefix = 0;
return(mnem);
}
@@ -383,155 +409,10 @@ static char *WritePitch(int env, int pitch1, int pitch2, int split, int final)
} // end of WritePitch
-#ifdef USE_MBROLA_LIB
-
-static void MbrolaMarker(int type, int char_posn, int length, int value)
-{//=====================================================================
-
- MarkerEvent(type,(char_posn & 0xffffff) | (length << 24),value,outbuf);
-
-}
-
-
-static void MbrolaEmbedded(int &embix, int sourceix)
-{//=================================================
- // There were embedded commands in the text at this point
- unsigned int word; // bit 7=last command for this word, bits 5,6 sign, bits 0-4 command
- unsigned int value;
- int command;
- int sign=0;
-
- do {
- word = embedded_list[embix++];
- value = word >> 8;
- command = word & 0x1f;
-
- if((word & 0x60) == 0x60)
- sign = -1;
- else
- if((word & 0x60) == 0x40)
- sign = 1;
-
- if(command < N_EMBEDDED_VALUES)
- {
- if(sign == 0)
- embedded_value[command] = value;
- else
- embedded_value[command] += (value * sign);
- }
-
- switch(command & 0x1f)
- {
- case EMBED_M: // named marker
- MbrolaMarker(espeakEVENT_MARK, (sourceix & 0x7ff) + clause_start_char, 0, value);
- break;
- }
- } while ((word & 0x80) == 0);
-}
-
-
-#ifdef PLATFORM_WINDOWS
-static int MbrolaSynth(char *p_mbrola)
-{//===================================
-// p_mbrola is a string of mbrola pho lines - Windows
- int len;
- int finished;
- int result=0;
-
- if(synth_callback == NULL)
- return(1);
-
- if(p_mbrola == NULL)
- flush_MBR();
- else
- result = write_MBR(p_mbrola);
-
-
- finished = 0;
- while(!finished && ((len = read_MBR((short *)outbuf, outbuf_size/2)) > 0))
- {
- out_ptr = outbuf + len*2;
-
- if(event_list)
- {
- event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
- event_list[event_list_ix].user_data = 0;
- }
- count_samples += len;
- finished = synth_callback((short *)outbuf, len, event_list);
- event_list_ix=0;
- }
-
- if(finished)
- {
- // cancelled by user, discard any unused mbrola speech
- flush_MBR();
- while((len = read_MBR((short *)outbuf, outbuf_size/2)) > 0);
- }
- return(finished);
-} // end of SynthMbrola
-#else
-
-static int MbrolaSynth(char *p_mbrola)
-{//===================================
-// p_mbrola is a string of mbrola pho lines - Linux
-
-// This is wrong
-// It must be called from WavegenFill()
-
- int len;
- int finished;
- int result=0;
-
- if(synth_callback == NULL)
- return(1);
-
- if(p_mbrola == NULL)
- mbrolib_flush(mb_handle);
- else
- result = mbrolib_write(mb_handle,p_mbrola,strlen(p_mbrola));
-
-
- finished = 0;
- while(!finished && (mbrolib_read(mb_handle, (short *)out_ptr, (out_end - out_ptr)/2, &len) == MBROLIB_OK))
- {
- if(len == 0)
- break;
-
- out_ptr += (len*2);
-
- if(event_list)
- {
- event_list[event_list_ix].type = espeakEVENT_LIST_TERMINATED; // indicates end of event list
- event_list[event_list_ix].user_data = 0;
- }
- count_samples += len;
- finished = synth_callback((short *)outbuf, len, event_list);
- event_list_ix=0;
- }
-
- if(finished)
- {
- // cancelled by user, discard any unused mbrola speech
- mbrolib_flush(mb_handle);
- while(mbrolib_read(mb_handle, (short *)outbuf, outbuf_size/2, &len) == MBROLIB_OK)
- {
- if(len == 0)
- break;
- }
- }
- return(finished);
-} // end of SynthMbrola
-#endif // not windows
-#endif // USE_MBROLA_LIB
-
-
-
-void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
-{//======================================================================
+int MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola)
+{//=================================================================================
// Generate a mbrola pho file
unsigned int name;
- int phix;
int len;
int len1;
PHONEME_TAB *ph;
@@ -539,7 +420,8 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
PHONEME_TAB *ph_prev;
PHONEME_LIST *p;
PHONEME_LIST *next;
- PHONEME_LIST *prev;
+ PHONEME_DATA phdata;
+ FMT_PARAMS fmtp;
int pause = 0;
int released;
int name2;
@@ -547,75 +429,76 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
int done;
int len_percent;
const char *final_pitch;
- char buf[80];
+ char *ptr;
char mbr_buf[120];
-#ifdef USE_MBROLA_LIB
- int embedded_ix=0;
- int word_count=0;
+ static int phix;
+ static int embedded_ix;
+ static int word_count;
- event_list_ix = 0;
- out_ptr = outbuf;
-#ifdef PLATFORM_WINDOWS
- setNoError_MBR(1); // don't stop on phoneme errors
-#endif
-#else
-// fprintf(f_mbrola,";; v=%.2f\n",(float)(mbrola_control & 0xff)/16.0); // ;; v= has no effect on mbrola
-#endif
+ if (!resume) {
+ phix = 1;
+ embedded_ix = 0;
+ word_count = 0;
+ }
- for(phix=1; phix < n_phonemes; phix++)
+ while (phix < n_phonemes)
{
- mbr_buf[0] = 0;
+ if (WcmdqFree() < MIN_WCMDQ)
+ return 1;
+
+ ptr = mbr_buf;
p = &plist[phix];
next = &plist[phix+1];
- prev = &plist[phix-1];
ph = p->ph;
ph_prev = plist[phix-1].ph;
ph_next = plist[phix+1].ph;
-#ifdef USE_MBROLA_LIB
if(p->synthflags & SFLAG_EMBEDDED)
{
- MbrolaEmbedded(embedded_ix, p->sourceix);
+ DoEmbedded(&embedded_ix, p->sourceix);
}
- if(p->newword & 4)
- MbrolaMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences);
+ if(p->newword & 4)
+ DoMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences);
if(p->newword & 1)
- MbrolaMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++);
-#endif
+ DoMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++);
name = GetMbrName(p,ph,ph_prev,ph_next,&name2,&len_percent,&control);
if(control & 1)
phix++;
- if(name == 0)
+ if(name == 0) {
+ phix++;
continue; // ignore this phoneme
+ }
if((ph->type == phPAUSE) && (name == ph->mnemonic))
{
// a pause phoneme, which has not been changed by the translation
name = '_';
- len = (p->length * speed.speed_factor1)/256;
+ len = (p->length * speed.pause_factor)/256;
// if(len == 0) continue;
if(len == 0)
len = 1;
}
else
- len = (80 * speed.speed_factor2)/256;
+ len = (80 * speed.wav_factor)/256;
-#ifdef USE_MBROLA_LIB
- MbrolaMarker(espeakEVENT_PHONEME, (p->sourceix & 0x7ff) + clause_start_char, 0, ph->mnemonic);
-#endif
+ if(ph->code != phonEND_WORD)
+ {
+ char phoneme_name[16];
+ WritePhMnemonic(phoneme_name, p->ph, p, option_phoneme_events & espeakINITIALIZE_PHONEME_IPA, NULL);
+ DoPhonemeMarker(espeakEVENT_PHONEME, (p->sourceix & 0x7ff) + clause_start_char, 0, phoneme_name);
+ }
- sprintf(buf,"%s\t",WordToString(name));
- strcat(mbr_buf,buf);
+ ptr += sprintf(ptr,"%s\t",WordToString(name));
if(name2 == '_')
{
// add a pause after this phoneme
- pause = PauseLength(len_percent,0);
+ pause = len_percent;
name2 = 0;
}
@@ -635,17 +518,19 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
if(name2 == 0)
{
- sprintf(buf,"%d\t%s", len, WritePitch(p->env,p->pitch1,p->pitch2,0,0));
- strcat(mbr_buf,buf);
+ char *pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,0);
+ ptr += sprintf(ptr,"%d\t%s", len, pitch);
}
else
{
+ char *pitch;
+
+ pitch = WritePitch(p->env,p->pitch1,p->pitch2,len_percent,0);
len1 = (len * len_percent)/100;
- sprintf(buf,"%d\t%s", len1, WritePitch(p->env,p->pitch1,p->pitch2,len_percent,0));
- strcat(mbr_buf,buf);
+ ptr += sprintf(ptr,"%d\t%s", len1, pitch);
- sprintf(buf,"%s\t%d\t%s", WordToString(name2), len-len1, WritePitch(p->env,p->pitch1,p->pitch2,-len_percent,0));
- strcat(mbr_buf,buf);
+ pitch = WritePitch(p->env,p->pitch1,p->pitch2,-len_percent,0);
+ ptr += sprintf(ptr,"%s\t%d\t%s", WordToString(name2), len-len1, pitch);
}
done = 1;
break;
@@ -655,23 +540,25 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
if(next->type==phVOWEL) released = 1;
if(next->type==phLIQUID && !next->newword) released = 1;
- if(released)
- len = DoSample(p->ph,next->ph,2,0,-1);
- else
- len = DoSample(p->ph,phoneme_tab[phonPAUSE],2,0,-1);
+ if(released == 0)
+ p->synthflags |= SFLAG_NEXT_PAUSE;
+ InterpretPhoneme(NULL, 0, p, &phdata, NULL);
+ len = DoSample3(&phdata, 0, -1);
+
len = (len * 1000)/samplerate; // convert to mS
len += PauseLength(p->prepause,1);
break;
case phVSTOP:
- len = (80 * speed.speed_factor2)/256;
+ len = (80 * speed.wav_factor)/256;
break;
case phFRICATIVE:
len = 0;
+ InterpretPhoneme(NULL, 0, p, &phdata, NULL);
if(p->synthflags & SFLAG_LENGTHEN)
- len = DoSample(ph,ph_next,2,p->length,-1); // play it twice for [s:] etc.
- len += DoSample(ph,ph_next,2,p->length,-1);
+ len = DoSample3(&phdata, p->length, -1); // play it twice for [s:] etc.
+ len += DoSample3(&phdata, p->length, -1);
len = (len * 1000)/samplerate; // convert to mS
break;
@@ -679,7 +566,11 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
case phNASAL:
if(next->type != phVOWEL)
{
- len = DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,-1);
+ memset(&fmtp, 0, sizeof(fmtp));
+ InterpretPhoneme(NULL, 0, p, &phdata, NULL);
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ len = DoSpect2(p->ph, 0, &fmtp, p, -1);
+// len = DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,-1);
len = (len * 1000)/samplerate;
if(next->type == phPAUSE)
len += 50;
@@ -701,60 +592,139 @@ void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola)
if(name2 != 0)
{
len1 = (len * len_percent)/100;
- sprintf(buf,"%d\n%s\t",len1,WordToString(name2));
- strcat(mbr_buf,buf);
+ ptr += sprintf(ptr,"%d\n%s\t",len1,WordToString(name2));
len -= len1;
}
- sprintf(buf,"%d%s\n",len,final_pitch);
- strcat(mbr_buf,buf);
+ ptr += sprintf(ptr,"%d%s\n",len,final_pitch);
}
if(pause)
{
- sprintf(buf,"_ \t%d\n",PauseLength(pause,0));
- strcat(mbr_buf,buf);
+ len += PauseLength(pause,0);
+ ptr += sprintf(ptr,"_ \t%d\n",PauseLength(pause,0));
pause = 0;
}
if(f_mbrola)
{
- fwrite(mbr_buf,1,strlen(mbr_buf),f_mbrola); // write .pho to a file
+ fwrite(mbr_buf,1,(ptr-mbr_buf),f_mbrola); // write .pho to a file
}
else
{
-#ifdef USE_MBROLA_LIB
- if(MbrolaSynth(mbr_buf) != 0)
- return;
-#endif
+ int res = write_MBR(mbr_buf);
+ if (res < 0)
+ return 0; /* don't get stuck on error */
+ if (res == 0)
+ return 1;
+ wcmdq[wcmdq_tail][0] = WCMD_MBROLA_DATA;
+ wcmdq[wcmdq_tail][1] = len;
+ WcmdqInc();
}
+
+ phix++;
}
-#ifdef USE_MBROLA_LIB
- MbrolaSynth(NULL);
-#endif
+ if(!f_mbrola)
+ {
+ flush_MBR();
+
+ // flush the mbrola output buffer
+ wcmdq[wcmdq_tail][0] = WCMD_MBROLA_DATA;
+ wcmdq[wcmdq_tail][1] = 500;
+ WcmdqInc();
+ }
+
+ return 0;
} // end of MbrolaTranslate
-#ifdef TEST_MBROLA
+int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
+{//==================================================================
+ FILE *f_mbrola = NULL;
-static PHONEME_LIST mbrola_phlist;
-static int mbrola_n_ph;
-static int mbrola_phix;
+ if(*n_ph == 0)
+ return(0);
+ if(option_mbrola_phonemes)
+ {
+ // send mbrola data to a file, not to the mbrola library
+ f_mbrola = f_trans;
+ }
-int MbrolaFill(int fill_zeros)
-{//===========================
+ int again = MbrolaTranslate(phoneme_list, *n_ph, resume, f_mbrola);
+ if (!again)
+ *n_ph = 0;
+ return again;
}
-int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
-{//==================================================================
- if(resume == 0)
+
+int MbrolaFill(int length, int resume, int amplitude)
+{//==================================================
+// Read audio data from Mbrola (length is in millisecs)
+
+ static int n_samples;
+ int req_samples, result;
+ int ix;
+ short value16;
+ int value;
+
+ if (!resume)
+ n_samples = samplerate * length / 1000;
+
+ req_samples = (out_end - out_ptr)/2;
+ if (req_samples > n_samples)
+ req_samples = n_samples;
+ result = read_MBR((short *)out_ptr, req_samples);
+ if (result <= 0)
+ return 0;
+
+ for(ix=0; ix < result; ix++)
{
- mbrola_phlist = phoneme_list;
- mbrola_n_ph = n_ph;
- mbrola_phix = 0;
+ value16 = out_ptr[0] + (out_ptr[1] << 8);
+ value = value16 * amplitude;
+ value = value / 40; // adjust this constant to give a suitable amplitude for mbrola voices
+ if(value > 0x7fff)
+ value = 0x7fff;
+ if(value < -0x8000)
+ value = 0x8000;
+ out_ptr[0] = value;
+ out_ptr[1] = value >> 8;
+ out_ptr += 2;
}
+ n_samples -= result;
+ return n_samples ? 1 : 0;
+}
- resume(0); // finished phoneme list
+
+void MbrolaReset(void)
+{//===================
+// Reset the Mbrola engine and flush the pending audio
+
+ reset_MBR();
}
-#endif
+
+#else // INCLUDE_MBROLA
+
+// mbrola interface is not compiled, provide dummy functions.
+
+espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate)
+{
+ return(EE_INTERNAL_ERROR);
+}
+
+int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
+{
+ return(0);
+}
+
+int MbrolaFill(int length, int resume, int amplitude)
+{
+ return(0);
+}
+
+void MbrolaReset(void)
+{
+}
+
+
+#endif // INCLUDE_MBROLA
diff --git a/navit/support/espeak/synthdata.c b/navit/support/espeak/synthdata.c
index 4f8234beb..4508bab92 100755..100644
--- a/navit/support/espeak/synthdata.c
+++ b/navit/support/espeak/synthdata.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2013 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
+#include <stdbool.h>
#include <wctype.h>
#include <string.h>
@@ -35,10 +36,14 @@
#include "translate.h"
#include "wave.h"
-const char *version_string = "1.41.01 25.Aug.09";
-const int version_phdata = 0x014100;
+#include "synthdata.h"
+
+const char *version_string = "1.48.03 04.Mar.14";
+const int version_phdata = 0x014801;
int option_device_number = -1;
+FILE *f_logespeak = NULL;
+int logging_type;
// copy the current phoneme table into here
int n_phoneme_tab;
@@ -46,8 +51,8 @@ int current_phoneme_table;
PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
unsigned char phoneme_tab_flags[N_PHONEME_TAB]; // bit 0: not inherited
-static unsigned int *phoneme_index=NULL;
-char *spects_data=NULL;
+USHORT *phoneme_index=NULL;
+char *phondata_ptr=NULL;
unsigned char *wavefile_data=NULL;
static unsigned char *phoneme_tab_data = NULL;
@@ -65,12 +70,12 @@ int vowel_transition[4];
int vowel_transition0;
int vowel_transition1;
-int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which);
+extern int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which);
-static char *ReadPhFile(void *ptr, const char *fname)
-{//==================================================
+static char *ReadPhFile(void *ptr, const char *fname, int *size)
+{//=============================================================
FILE *f_in;
char *p;
unsigned int length;
@@ -78,7 +83,7 @@ static char *ReadPhFile(void *ptr, const char *fname)
sprintf(buf,"%s%c%s",path_home,PATHSEP,fname);
length = GetFileLength(buf);
-
+
if((f_in = fopen(buf,"rb")) == NULL)
{
fprintf(stderr,"Can't read data file: '%s'\n",buf);
@@ -87,7 +92,7 @@ static char *ReadPhFile(void *ptr, const char *fname)
if(ptr != NULL)
Free(ptr);
-
+
if((p = Alloc(length)) == NULL)
{
fclose(f_in);
@@ -100,31 +105,41 @@ static char *ReadPhFile(void *ptr, const char *fname)
}
fclose(f_in);
+ if(size != NULL)
+ *size = length;
return(p);
} // end of ReadPhFile
-int LoadPhData()
-{//=============
+int LoadPhData(int *srate)
+{//========================
int ix;
int n_phonemes;
int version;
int result = 1;
+ int length;
+ int rate;
unsigned char *p;
+ int *pw;
- if((phoneme_tab_data = (unsigned char *)ReadPhFile((void *)(phoneme_tab_data),"phontab")) == NULL)
+ if((phoneme_tab_data = (unsigned char *)ReadPhFile((void *)(phoneme_tab_data),"phontab",NULL)) == NULL)
return(-1);
- if((phoneme_index = (unsigned int *)ReadPhFile((void *)(phoneme_index),"phonindex")) == NULL)
+ if((phoneme_index = (USHORT *)ReadPhFile((void *)(phoneme_index),"phonindex",NULL)) == NULL)
return(-1);
- if((spects_data = ReadPhFile((void *)(spects_data),"phondata")) == NULL)
+ if((phondata_ptr = ReadPhFile((void *)(phondata_ptr),"phondata",NULL)) == NULL)
return(-1);
- wavefile_data = (unsigned char *)spects_data;
+ if((tunes = (TUNE *)ReadPhFile((void *)(tunes),"intonations",&length)) == NULL)
+ return(-1);
+ wavefile_data = (unsigned char *)phondata_ptr;
+ n_tunes = length / sizeof(TUNE);
- // read the version number from the first 4 bytes of phondata
- version = 0;
+ // read the version number and sample rate from the first 8 bytes of phondata
+ version = 0; // bytes 0-3, version number
+ rate = 0; // bytes 4-7, sample rate
for(ix=0; ix<4; ix++)
{
version += (wavefile_data[ix] << (ix*8));
+ rate += (wavefile_data[ix+4] << (ix*8));
}
if(version != version_phdata)
@@ -142,7 +157,9 @@ int LoadPhData()
n_phonemes = p[0];
phoneme_tab_list[ix].n_phonemes = p[0];
phoneme_tab_list[ix].includes = p[1];
- p += 4;
+ pw = (int *)p;
+ phoneme_tab_list[ix].equivalence_tables = Reverse4Bytes(pw[1]);
+ p += 8;
memcpy(phoneme_tab_list[ix].name,p,N_PHONEME_TAB_NAME);
p += N_PHONEME_TAB_NAME;
phoneme_tab_list[ix].phoneme_tab_ptr = (PHONEME_TAB *)p;
@@ -152,6 +169,10 @@ int LoadPhData()
if(phoneme_tab_number >= n_phoneme_tables)
phoneme_tab_number = 0;
+ if(srate != NULL)
+ {
+ *srate = rate;
+ }
return(result);
} // end of LoadPhData
@@ -160,10 +181,12 @@ void FreePhData(void)
{//==================
Free(phoneme_tab_data);
Free(phoneme_index);
- Free(spects_data);
+ Free(phondata_ptr);
+ Free(tunes);
phoneme_tab_data=NULL;
phoneme_index=NULL;
- spects_data=NULL;
+ phondata_ptr=NULL;
+ tunes=NULL;
}
@@ -202,172 +225,9 @@ int LookupPhonemeString(const char *string)
-static unsigned int LookupSound2(int index, unsigned int other_phcode, int control)
-{//================================================================================
-// control=1 get formant transition data only
- unsigned int code;
- unsigned int value, value2;
-
- while((value = phoneme_index[index++]) != 0)
- {
- if((code = (value & 0xff)) == other_phcode)
- {
- while(((value2 = phoneme_index[index]) != 0) && ((value2 & 0xff) < 8))
- {
- switch(value2 & 0xff)
- {
- case 0:
- // next entry is a wavefile to be played along with the synthesis
- if(control==0)
- {
- wavefile_ix = value2 >> 8;
- }
- break;
- case 1:
- if(control==0)
- {
- seq_len_adjust = value2 >> 8;
- }
- break;
- case 2:
- if(control==0)
- {
- seq_len_adjust = value2 >> 8;
- seq_len_adjust = -seq_len_adjust;
- }
- break;
- case 3:
- if(control==0)
- {
- wavefile_amp = value2 >> 8;
- }
- break;
- case 4:
- // formant transition data, 2 words
- vowel_transition[0] = value2 >> 8;
- vowel_transition[1] = phoneme_index[index++ + 1];
- break;
- case 5:
- // formant transition data, 2 words
- vowel_transition[2] = value2 >> 8;
- vowel_transition[3] = phoneme_index[index++ + 1];
- break;
- }
- index++;
- }
- return(value >> 8);
- }
- else
- if((code == 4) || (code == 5))
- {
- // formant transition data, ignore next word of data
- index++;
- }
- }
- return(3); // not found
-} // end of LookupSound2
-
-
-unsigned int LookupSound(PHONEME_TAB *this_ph, PHONEME_TAB *other_ph, int which, int *match_level, int control)
-{//============================================================================================================
- // follows, 1 other_ph preceeds this_ph, 2 other_ph follows this_ph
- // control: 1= get formant transition data only
- int spect_list;
- int spect_list2;
- int s_list;
- unsigned char virtual_ph;
- int result;
- int level=0;
- unsigned int other_code;
- unsigned int other_virtual;
-
- if(control==0)
- {
- wavefile_ix = 0;
- wavefile_amp = 32;
- seq_len_adjust = 0;
- }
- memset(vowel_transition,0,sizeof(vowel_transition));
-
- other_code = other_ph->code;
- if(phoneme_tab[other_code]->type == phPAUSE)
- other_code = phonPAUSE_SHORT; // use this version of Pause for matching
-
- if(which==1)
- {
- spect_list = this_ph->after;
- virtual_ph = this_ph->start_type;
- spect_list2 = phoneme_tab[virtual_ph]->after;
- other_virtual = other_ph->end_type;
- }
- else
- {
- spect_list = this_ph->before;
- virtual_ph = this_ph->end_type;
- spect_list2 = phoneme_tab[virtual_ph]->before;
- other_virtual = other_ph->start_type;
- }
-
- result = 3;
- // look for ph1-ph2 combination
- if((s_list = spect_list) != 0)
- {
- if((result = LookupSound2(s_list,other_code,control)) != 3)
- {
- level = 2;
- }
- else
- if(other_virtual != 0)
- {
- if((result = LookupSound2(spect_list,other_virtual,control)) != 3)
- {
- level = 1;
- }
- }
- }
- // not found, look in a virtual phoneme if one is given for this phoneme
- if((result==3) && (virtual_ph != 0) && ((s_list = spect_list2) != 0))
- {
- if((result = LookupSound2(s_list,other_code,control)) != 3)
- {
- level = 1;
- }
- else
- if(other_virtual != 0)
- {
- if((result = LookupSound2(spect_list2,other_virtual,control)) != 3)
- {
- level = 1;
- }
- }
- }
-
- if(match_level != NULL)
- *match_level = level;
-
- if(result==0)
- return(0); // NULL was given in the phoneme source
-
- // note: values = 1 indicates use the default for this phoneme, even though we found a match
- // which set a secondary reference
- if(result >= 4)
- {
- // values 1-3 can be used for special codes
- // 1 = DFT from the phoneme source file
- return(result);
- }
-
- // no match found for other_ph, return the default
- return(LookupSound2(this_ph->spect,phonPAUSE,control));
-
-} // end of LookupSound
-
-
-
-frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
- int which, int *match_level, int *n_frames, PHONEME_LIST *plist)
-{//=========================================================================================================
+frameref_t *LookupSpect(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, int *n_frames, PHONEME_LIST *plist)
+{//===================================================================================================================
int ix;
int nf;
int nf1;
@@ -378,19 +238,10 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB
int length_factor;
SPECT_SEQ *seq, *seq2;
SPECT_SEQK *seqk, *seqk2;
- PHONEME_TAB *next2_ph;
frame_t *frame;
static frameref_t frames_buf[N_SEQ_FRAMES];
-
- PHONEME_TAB *other_ph;
- if(which == 1)
- other_ph = prev_ph;
- else
- other_ph = next_ph;
- if((ix = LookupSound(this_ph,other_ph,which,match_level,0)) < 4)
- return(NULL);
- seq = (SPECT_SEQ *)(&spects_data[ix]);
+ seq = (SPECT_SEQ *)(&phondata_ptr[fmt_params->fmt_addr]);
seqk = (SPECT_SEQK *)seq;
nf = seq->n_frames;
@@ -398,8 +249,8 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB
if(nf >= N_SEQ_FRAMES)
nf = N_SEQ_FRAMES - 1;
+ seq_len_adjust = fmt_params->fmt2_lenadj + fmt_params->fmt_length;
seq_break = 0;
- length1 = 0;
for(ix=0; ix<nf; ix++)
{
@@ -427,50 +278,25 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB
nf -= seq_break;
}
}
-
+
// do we need to modify a frame for blending with a consonant?
- if(this_ph->type == phVOWEL)
+ if((this_ph->type == phVOWEL) && (fmt_params->fmt2_addr == 0) && (fmt_params->use_vowelin))
{
- if((which==2) && ((frames[nf-1].frflags & FRFLAG_BREAK) == 0))
- {
- // lookup formant transition for the following phoneme
-
- if((*match_level == 0) || (next_ph->type == phNASAL))
- {
- LookupSound(next_ph,this_ph,1,NULL,1);
- seq_len_adjust += FormantTransition2(frames,&nf,vowel_transition[2],vowel_transition[3],next_ph,which);
- }
- else
- if(next_ph->phflags == phVOWEL2)
- {
- // not really a consonant, rather a coloured vowel
- if(LookupSound(next_ph,this_ph,1,NULL,1) == 0)
- {
- next2_ph = plist[2].ph;
- LookupSound(next2_ph,next_ph,1,NULL,1);
- seq_len_adjust += FormantTransition2(frames,&nf,vowel_transition[2],vowel_transition[3],next2_ph,which);
- }
- }
- }
- else
- {
- if(*match_level == 0)
- seq_len_adjust = FormantTransition2(frames,&nf,vowel_transition0,vowel_transition1,prev_ph,which);
- }
+ seq_len_adjust += FormantTransition2(frames,&nf,fmt_params->transition0,fmt_params->transition1,NULL,which);
}
+ length1 = 0;
nf1 = nf - 1;
for(ix=0; ix<nf1; ix++)
length1 += frames[ix].length;
-
- if((wavefile_ix != 0) && ((wavefile_ix & 0x800000)==0))
+ if(fmt_params->fmt2_addr != 0)
{
// a secondary reference has been returned, which is not a wavefile
// add these spectra to the main sequence
- seq2 = (SPECT_SEQ *)(&spects_data[wavefile_ix]);
+ seq2 = (SPECT_SEQ *)(&phondata_ptr[fmt_params->fmt2_addr]);
seqk2 = (SPECT_SEQK *)seq2;
-
+
// first frame of the addition just sets the length of the last frame of the main seq
nf--;
for(ix=0; ix<seq2->n_frames; ix++)
@@ -490,25 +316,25 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB
}
wavefile_ix = 0;
}
-
- if((this_ph->type == phVOWEL) && (length1 > 0))
+
+ if(length1 > 0)
{
if(which==2)
{
// adjust the length of the main part to match the standard length specified for the vowel
// less the front part of the vowel and any added suffix
-
- length_std = this_ph->std_length + seq_len_adjust - 45;
+
+ length_std = fmt_params->std_length + seq_len_adjust - 45;
if(length_std < 10)
length_std = 10;
if(plist->synthflags & SFLAG_LENGTHEN)
- length_std += phoneme_tab[phonLENGTHEN]->std_length; // phoneme was followed by an extra : symbol
+ length_std += (phoneme_tab[phonLENGTHEN]->std_length * 2); // phoneme was followed by an extra : symbol
// can adjust vowel length for stressed syllables here
length_factor = (length_std * 256)/ length1;
-
+
for(ix=0; ix<nf1; ix++)
{
frames[ix].length = (frames[ix].length * length_factor)/256;
@@ -516,22 +342,29 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB
}
else
{
- // front of a vowel
- if(*match_level == 0)
+ if(which == 1)
{
- // allow very short vowels to have shorter front parts
- if(this_ph->std_length < 130)
- frames[0].length = (frames[0].length * this_ph->std_length)/130;
+ // front of a vowel
+ if(fmt_params->fmt_control == 1)
+ {
+ // This is the default start of a vowel.
+ // Allow very short vowels to have shorter front parts
+ if(fmt_params->std_length < 130)
+ frames[0].length = (frames[0].length * fmt_params->std_length)/130;
+ }
}
-
- if(seq_len_adjust != 0)
+ else
{
- length_std = 0;
- for(ix=0; ix<nf1; ix++)
+ //not a vowel
+ if(fmt_params->std_length > 0)
{
- length_std += frames[ix].length;
+ seq_len_adjust += (fmt_params->std_length - length1);
}
- length_factor = ((length_std + seq_len_adjust) * 256)/length_std;
+ }
+
+ if(seq_len_adjust != 0)
+ {
+ length_factor = ((length1 + seq_len_adjust) * 256)/length1;
for(ix=0; ix<nf1; ix++)
{
frames[ix].length = (frames[ix].length * length_factor)/256;
@@ -539,17 +372,21 @@ frameref_t *LookupSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB
}
}
}
-
+
*n_frames = nf;
return(frames);
} // end of LookupSpect
-unsigned char *LookupEnvelope(int ix)
-{//================================
- if(ix==0)
- return(NULL);
- return((unsigned char *)&spects_data[phoneme_index[ix]]);
+
+unsigned char *GetEnvelope(int index)
+{//==================================
+ if(index==0)
+ {
+ fprintf(stderr,"espeak: No envelope\n");
+ return(envelope_data[0]); // not found, use a default envelope
+ }
+ return((unsigned char *)&phondata_ptr[index]);
}
@@ -640,6 +477,8 @@ void LoadConfig(void)
char *p;
char string[200];
+ logging_type = 0;
+
for(ix=0; ix<N_SOUNDICON_SLOTS; ix++)
{
soundicon_tab[ix].filename = NULL;
@@ -654,6 +493,14 @@ void LoadConfig(void)
while(fgets(buf,sizeof(buf),f)!=NULL)
{
+ if(buf[0] == '/') continue;
+
+ if(memcmp(buf,"log",3)==0)
+ {
+ if(sscanf(&buf[4],"%d %s",&logging_type,string)==2)
+ f_logespeak = fopen(string,"w");
+ }
+ else
if(memcmp(buf,"tone",4)==0)
{
ReadTonePoints(&buf[5],tone_points);
@@ -661,7 +508,7 @@ void LoadConfig(void)
else
if(memcmp(buf,"pa_device",9)==0)
{
- sscanf(&buf[7],"%d",&option_device_number);
+ sscanf(&buf[10],"%d",&option_device_number);
}
else
if(memcmp(buf,"soundicon",9)==0)
@@ -680,3 +527,764 @@ void LoadConfig(void)
fclose(f);
} // end of LoadConfig
+
+
+
+PHONEME_DATA this_ph_data;
+
+
+static void InvalidInstn(PHONEME_TAB *ph, int instn)
+{//====================================================
+ fprintf(stderr,"Invalid instruction %.4x for phoneme '%s'\n", instn, WordToString(ph->mnemonic));
+}
+
+
+static bool StressCondition(Translator *tr, PHONEME_LIST *plist, int condition, int control)
+{//========================================================================================
+// condition:
+// 0 if diminished, 1 if unstressed, 2 if not stressed, 3 if stressed, 4 if max stress
+
+ int stress_level;
+ PHONEME_LIST *pl;
+ static int condition_level[4] = {1,2,4,15};
+
+ if(phoneme_tab[plist[0].phcode]->type == phVOWEL)
+ {
+ pl = plist;
+ }
+ else
+ {
+ // consonant, get stress from the following vowel
+ if(phoneme_tab[plist[1].phcode]->type == phVOWEL)
+ {
+ pl = &plist[1];
+ }
+ else
+ return(false); // no stress elevel for this consonant
+ }
+
+ stress_level = pl->stresslevel & 0xf;
+
+ if(tr != NULL)
+ {
+ if((control & 1) && (plist->synthflags & SFLAG_DICTIONARY) && ((tr->langopts.param[LOPT_REDUCE] & 1)==0))
+ {
+ // change phoneme. Don't change phonemes which are given for the word in the dictionary.
+ return(false);
+ }
+
+ if((tr->langopts.param[LOPT_REDUCE] & 0x2) && (stress_level >= pl->wordstress))
+ {
+ // treat the most stressed syllable in an unstressed word as stressed
+ stress_level = 4;
+ }
+ }
+
+ if(condition == 4)
+ {
+ return(stress_level >= pl->wordstress);
+ }
+
+ if(condition == 3)
+ {
+ // if stressed
+ if(stress_level > 3)
+ return(true);
+ }
+ else
+ {
+ if(stress_level < condition_level[condition])
+ return(true);
+ }
+ return(false);
+
+} // end of StressCondition
+
+
+static int CountVowelPosition(PHONEME_LIST *plist)
+{//===============================================
+ int count = 0;
+
+ for(;;)
+ {
+ if(plist->ph->type == phVOWEL)
+ count++;
+ if(plist->sourceix != 0)
+ break;
+ plist--;
+ }
+ return(count);
+} // end of CoundVowelPosition
+
+
+static bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist, USHORT *p_prog, WORD_PH_DATA *worddata)
+{//========================================================================================================================
+ int which;
+ int ix;
+ unsigned int data;
+ int instn;
+ int instn2;
+ int count;
+ PHONEME_TAB *ph;
+ PHONEME_LIST *plist_this;
+
+ // instruction: 2xxx, 3xxx
+
+ // bits 8-10 = 0 to 5, which phoneme, =6 the 'which' information is in the next instruction.
+ // bit 11 = 0, bits 0-7 are a phoneme code
+ // bit 11 = 1, bits 5-7 type of data, bits 0-4 data value
+
+ // bits 8-10 = 7, other conditions
+
+ instn = (*p_prog) & 0xfff;
+ data = instn & 0xff;
+ instn2 = instn >> 8;
+
+ if(instn2 < 14)
+ {
+ plist_this = plist;
+ which = (instn2) % 7;
+
+ if(which==6)
+ {
+ // the 'which' code is in the next instruction
+ p_prog++;
+ which = (*p_prog);
+ }
+
+ if(which==4)
+ {
+ // nextPhW not word boundary
+ if(plist[1].sourceix)
+ return(false);
+ }
+ if(which==5)
+ {
+ // prevPhW, not word boundary
+ if(plist[0].sourceix)
+ return(false);
+ }
+ if(which==6)
+ {
+ // next2PhW, not word boundary
+ if(plist[1].sourceix || plist[2].sourceix)
+ return(false);
+ }
+
+
+ switch(which)
+ {
+ case 0: // prevPh
+ case 5: // prevPhW
+ plist--;
+ break;
+
+ case 1: // thisPh
+ break;
+
+ case 2: // nextPh
+ case 4: // nextPhW
+ plist++;
+ break;
+
+ case 3: // next2Ph
+ case 6: // next2PhW
+ plist += 2;
+ break;
+
+ case 7:
+ // nextVowel, not word boundary
+ for(which=1;;which++)
+ {
+ if(plist[which].sourceix)
+ return(false);
+ if(phoneme_tab[plist[which].phcode]->type == phVOWEL)
+ {
+ plist = &plist[which];
+ break;
+ }
+ }
+ break;
+
+ case 8: // prevVowel in this word
+ if((worddata==NULL) || (worddata->prev_vowel.ph == NULL))
+ return(false); // no previous vowel
+ plist = &(worddata->prev_vowel);
+ break;
+
+ case 9: // next3PhW
+ for(ix=1; ix<=3; ix++)
+ {
+ if(plist[ix].sourceix)
+ return(false);
+ }
+ plist = &plist[3];
+ break;
+
+ case 10: // prev2PhW
+ if((plist[0].sourceix) || (plist[-1].sourceix))
+ return(false);
+ plist-=2;
+ break;
+ }
+
+ if((which == 0) || (which == 5))
+ {
+ if(plist->phcode == 1)
+ {
+ // This is a NULL phoneme, a phoneme has been deleted so look at the previous phoneme
+ plist--;
+ }
+ }
+
+ if(control & 0x100)
+ {
+ // "change phonemes" pass
+ plist->ph = phoneme_tab[plist->phcode];
+ }
+ ph = plist->ph;
+
+ if(instn2 < 7)
+ {
+ // 'data' is a phoneme number
+ if((phoneme_tab[data]->mnemonic == ph->mnemonic) == true)
+ return(true);
+ if((which == 0) && (ph->type == phVOWEL))
+ return(data == ph->end_type); // prevPh() match on end_type
+ return(data == ph->start_type); // thisPh() or nextPh(), match on start_type
+ }
+
+ data = instn & 0x1f;
+
+ switch(instn & 0xe0)
+ {
+ case 0x00:
+ // phoneme type, vowel, nasal, fricative, etc
+ return(ph->type == data);
+ break;
+
+ case 0x20:
+ // place of articulation
+ return(((ph->phflags >> 16) & 0xf) == data);
+ break;
+
+ case 0x40:
+ // is a bit set in phoneme flags
+ return((ph->phflags & (1 << data)) != 0);
+ break;
+
+ case 0x80:
+ switch(data)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return(StressCondition(tr, plist, data, 0));
+
+ case 5: // isBreak, Either pause phoneme, or (stop/vstop/vfric not followed by vowel or (liquid in same word))
+ return((ph->type == phPAUSE) || (plist_this->synthflags & SFLAG_NEXT_PAUSE));
+
+ case 6: // isWordStart
+ return(plist->sourceix != 0);
+
+ case 7: // notWordStart
+ return(plist->sourceix == 0);
+
+ case 8: // isWordEnd
+ return(plist[1].sourceix || (plist[1].ph->type == phPAUSE));
+ break;
+
+ case 9: // isAfterStress
+ if(plist->sourceix != 0)
+ return(false);
+ do {
+ plist--;
+ if((plist->stresslevel & 0xf) >= 4)
+ return(true);
+
+ } while (plist->sourceix == 0);
+ break;
+
+ case 10: // isNotVowel
+ return(ph->type != phVOWEL);
+
+ case 11: // isFinalVowel
+ for(;;)
+ {
+ plist++;
+ plist->ph = phoneme_tab[plist->phcode];
+ if(plist->sourceix != 0)
+ return(true); // start of next word, without finding another vowel
+ if(plist->ph->type == phVOWEL)
+ return(false);
+ }
+ break;
+
+ case 12: // isVoiced
+ return((ph->type == phVOWEL) || (ph->type == phLIQUID) || (ph->phflags & phVOICED));
+
+ case 13: // isFirstVowel
+ return(CountVowelPosition(plist)==1);
+
+ case 14: // isSecondVowel
+ return(CountVowelPosition(plist)==2);
+
+ case 15: // isSeqFlag1
+ // is this preceded by a sequence if 1 or more vowels which have 'flag1' ? (lang=hi)
+ if(plist->sourceix != 0)
+ return(false); // this is the first phoneme in the word, so no.
+
+ count = 0;
+ for(;;)
+ {
+ plist--;
+ if(plist->ph->type == phVOWEL)
+ {
+ if(plist->ph->phflags & phFLAG1)
+ count++;
+ else
+ break; // stop when we find a vowel without flag1
+ }
+ if(plist->sourceix != 0)
+ break;
+ }
+ return(count > 0);
+
+ case 0x10: // isTranslationGiven
+ return((plist->synthflags & SFLAG_DICTIONARY) != 0);
+ }
+ break;
+
+ }
+ return(false);
+ }
+ else
+ if(instn2 == 0xf)
+ {
+ // Other conditions
+ switch(data)
+ {
+ case 1: // PreVoicing
+ return(control & 1);
+ case 2: // KlattSynth
+ return(voice->klattv[0] != 0);
+ case 3: // MbrolaSynth
+ return(mbrola_name[0] != 0);
+ }
+ }
+ return(false);
+} // end of InterpretCondition
+
+
+static void SwitchOnVowelType(PHONEME_LIST *plist, PHONEME_DATA *phdata, USHORT **p_prog, int instn_type)
+{//========================================================================================================
+ USHORT *prog;
+ int voweltype;
+ signed char x;
+
+ if(instn_type == 2)
+ {
+ phdata->pd_control |= pd_FORNEXTPH;
+ voweltype = plist[1].ph->start_type; // SwitchNextVowelType
+ }
+ else
+ {
+ voweltype = plist[-1].ph->end_type; // SwitchPrevVowelType
+ }
+
+ voweltype -= phonVOWELTYPES;
+ if((voweltype >= 0) && (voweltype < 6))
+ {
+ prog = *p_prog + voweltype*2;
+ phdata->sound_addr[instn_type] = (((prog[1] & 0xf) << 16) + prog[2]) * 4;
+ x = (prog[1] >> 4) & 0xff;
+ phdata->sound_param[instn_type] = x; // sign extend
+ }
+
+ *p_prog += 12;
+} // end of SwitchVowelType
+
+
+int NumInstnWords(USHORT *prog)
+{//============================
+ int instn;
+ int instn2;
+ int instn_type;
+ int n;
+ int type2;
+ static const char n_words[16] = {0,1,0,0,1,1,0,1,1,2,4,0,0,0,0,0};
+
+ instn = *prog;
+ instn_type = instn >> 12;
+ if((n = n_words[instn_type]) > 0)
+ return(n);
+
+ switch(instn_type)
+ {
+ case 0:
+ if(((instn & 0xf00) >> 8) == i_IPA_NAME)
+ {
+ n = ((instn & 0xff) + 1) / 2;
+ return(n+1);
+ }
+ return(1);;
+
+ case 6:
+ type2 = (instn & 0xf00) >> 9;
+ if((type2 == 5) || (type2 == 6))
+ return(12); // switch on vowel type
+ return(1);
+
+ case 2:
+ case 3:
+ // a condition, check for a 2-word instruction
+ if(((n = instn & 0x0f00) == 0x600) || (n == 0x0d00))
+ return(2);
+ return(1);
+
+ default:
+ // instn_type 11 to 15, 2 words
+ instn2 = prog[2];
+ if((instn2 >> 12) == 0xf)
+ {
+ // This instruction is followed by addWav(), 2 more words
+ return(4);
+ }
+ if(instn2 == i_CONTINUE)
+ {
+ return(3);
+ }
+ return(2);
+ }
+} // end of NumInstnWords
+
+
+
+void InterpretPhoneme(Translator *tr, int control, PHONEME_LIST *plist, PHONEME_DATA *phdata, WORD_PH_DATA *worddata)
+{//===================================================================================================================
+// control:
+//bit 0: PreVoicing
+//bit 8: change phonemes
+ PHONEME_TAB *ph;
+ USHORT *prog;
+ USHORT instn;
+ int instn2;
+ int or_flag;
+ bool truth;
+ bool truth2;
+ int data;
+ int end_flag;
+ int ix;
+ signed char param_sc;
+
+ #define N_RETURN 10
+ int n_return=0;
+ USHORT *return_addr[N_RETURN]; // return address stack
+
+ ph = plist->ph;
+
+ if((worddata != NULL) && (plist->sourceix))
+ {
+ // start of a word, reset word data
+ worddata->prev_vowel.ph = NULL;
+ }
+
+ memset(phdata, 0, sizeof(PHONEME_DATA));
+ phdata->pd_param[i_SET_LENGTH] = ph->std_length;
+ phdata->pd_param[i_LENGTH_MOD] = ph->length_mod;
+
+ if(ph->program == 0)
+ {
+ return;
+ }
+
+ end_flag = 0;
+
+ for(prog = &phoneme_index[ph->program]; end_flag != 1; prog++)
+ {
+ instn = *prog;
+ instn2 = (instn >> 8) & 0xf;
+ or_flag = 0;
+
+ switch(instn >> 12)
+ {
+ case 0: // 0xxx
+ data = instn & 0xff;
+
+ if(instn2 == 0)
+ {
+ // instructions with no operand
+ switch(data)
+ {
+ case i_RETURN:
+ end_flag = 1;
+ break;
+
+ case i_CONTINUE:
+ break;
+
+ default:
+ InvalidInstn(ph,instn);
+ break;
+ }
+ }
+ else
+ if(instn2 == i_APPEND_IFNEXTVOWEL)
+ {
+ if(phoneme_tab[plist[1].phcode]->type == phVOWEL)
+ phdata->pd_param[i_APPEND_PHONEME] = data;
+ }
+ else
+ if(instn2 == i_ADD_LENGTH)
+ {
+ if(data & 0x80)
+ {
+ // a negative value, do sign extension
+ data = -(0x100 - data);
+ }
+ phdata->pd_param[i_SET_LENGTH] += data;
+ }
+ else
+ if(instn2 == i_IPA_NAME)
+ {
+ // followed by utf-8 characters, 2 per instn word
+ for(ix=0; (ix < data) && (ix < 16); ix += 2)
+ {
+ prog++;
+ phdata->ipa_string[ix] = prog[0] >> 8;
+ phdata->ipa_string[ix+1] = prog[0] & 0xff;
+ }
+ phdata->ipa_string[ix] = 0;
+ }
+ else
+ if(instn2 < N_PHONEME_DATA_PARAM)
+ {
+ if(instn2 == i_CHANGE_PHONEME2)
+ {
+ phdata->pd_param[i_CHANGE_PHONEME] = data; // also set ChangePhoneme
+ }
+ phdata->pd_param[instn2] = data;
+ if((instn2 == i_CHANGE_PHONEME) && (control & 0x100))
+ {
+ // found ChangePhoneme() in PhonemeList mode, exit
+ end_flag = 1;
+ }
+ }
+ else
+ {
+ InvalidInstn(ph,instn);
+ }
+ break;
+
+ case 1:
+ if(tr == NULL)
+ break; // ignore if in synthesis stage
+
+ if(instn2 < 8)
+ {
+ // ChangeIf
+ if(StressCondition(tr, plist, instn2 & 7, 1) == true)
+ {
+ phdata->pd_param[i_CHANGE_PHONEME] = instn & 0xff;
+ end_flag = 1; // change phoneme, exit
+ }
+ }
+ break;
+
+ case 2:
+ case 3:
+ // conditions
+ or_flag = 0;
+ truth = true;
+ while((instn & 0xe000) == 0x2000)
+ {
+ // process a sequence of conditions, using boolean accumulator
+ truth2 = InterpretCondition(tr, control, plist, prog, worddata);
+ prog += NumInstnWords(prog);
+ if(*prog == i_NOT)
+ {
+ truth2 = truth2 ^ 1;
+ prog++;
+ }
+
+ if(or_flag)
+ truth = truth || truth2;
+ else
+ truth = truth && truth2;
+ or_flag = instn & 0x1000;
+ instn = *prog;
+ }
+
+ if(truth == false)
+ {
+ if((instn & 0xf800) == i_JUMP_FALSE)
+ {
+ prog += instn & 0xff;
+ }
+ else
+ {
+ // instruction after a condition is not JUMP_FALSE, so skip the instruction.
+ prog += NumInstnWords(prog);
+ if((prog[0] & 0xfe00) == 0x6000)
+ prog++; // and skip ELSE jump
+ }
+ }
+ prog--;
+ break;
+
+ case 6:
+ // JUMP
+ switch(instn2 >> 1)
+ {
+ case 0:
+ prog += (instn & 0xff) - 1;
+ break;
+
+ case 4:
+ // conditional jumps should have been processed in the Condition section
+ break;
+
+ case 5: // NexttVowelStarts
+ SwitchOnVowelType(plist, phdata, &prog, 2);
+ break;
+
+ case 6: // PrevVowelTypeEndings
+ SwitchOnVowelType(plist, phdata, &prog, 3);
+ break;
+ }
+ break;
+
+ case 9:
+ data = ((instn & 0xf) << 16) + prog[1];
+ prog++;
+ switch(instn2)
+ {
+ case 1:
+ // call a procedure or another phoneme
+ if(n_return < N_RETURN)
+ {
+ return_addr[n_return++] = prog;
+ prog = &phoneme_index[data] - 1;
+ }
+ break;
+
+ case 2:
+ // pitch envelope
+ phdata->pitch_env = data;
+ break;
+
+ case 3:
+ // amplitude envelope
+ phdata->amp_env = data;
+ break;
+ }
+ break;
+
+ case 10: // Vowelin, Vowelout
+ if(instn2 == 1)
+ ix = 0;
+ else
+ ix = 2;
+
+ phdata->vowel_transition[ix] = ((prog[0] & 0xff) << 16) + prog[1];
+ phdata->vowel_transition[ix+1] = (prog[2] << 16) + prog[3];
+ prog += 3;
+ break;
+
+ case 11: // FMT
+ case 12: // WAV
+ case 13: // VowelStart
+ case 14: // VowelEnd
+ case 15: // addWav
+ instn2 = (instn >> 12) - 11;
+ phdata->sound_addr[instn2] = ((instn & 0xf) << 18) + (prog[1] << 2);
+ param_sc = phdata->sound_param[instn2] = (instn >> 4) & 0xff;
+ prog++;
+
+ if(prog[1] != i_CONTINUE)
+ {
+ if(instn2 < 2)
+ {
+ // FMT() and WAV() imply Return
+ end_flag = 1;
+ if((prog[1] >> 12) == 0xf)
+ {
+ // Return after the following addWav()
+ end_flag = 2;
+ }
+ }
+ else
+ if(instn2 ==pd_ADDWAV)
+ {
+ // addWav(), return if previous instruction was FMT() or WAV()
+ end_flag--;
+ }
+
+ if((instn2 == pd_VWLSTART) || (instn2 == pd_VWLEND))
+ {
+ // VowelStart or VowelEnding.
+ phdata->sound_param[instn2] = param_sc; // sign extend
+ }
+ }
+ break;
+
+ default:
+ InvalidInstn(ph,instn);
+ break;
+ }
+
+ if(ph->phflags & phSINGLE_INSTN)
+ {
+ end_flag = 1; // this phoneme has a one-instruction program, with an implicit Return
+ }
+
+ if((end_flag == 1) && (n_return > 0))
+ {
+ // return from called procedure or phoneme
+ end_flag = 0;
+ prog = return_addr[--n_return];
+ }
+ }
+
+ if((worddata != NULL) && (plist->type == phVOWEL))
+ {
+ memcpy(&worddata->prev_vowel, &plist[0], sizeof(PHONEME_LIST));
+ }
+
+#ifdef _ESPEAKEDIT
+ plist->std_length = phdata->pd_param[i_SET_LENGTH];
+ if(phdata->sound_addr[0] != 0)
+ {
+ plist->phontab_addr = phdata->sound_addr[0]; // FMT address
+ plist->sound_param = phdata->sound_param[0];
+ }
+ else
+ {
+ plist->phontab_addr = phdata->sound_addr[1]; // WAV address
+ plist->sound_param = phdata->sound_param[1];
+ }
+#endif
+} // end of InterpretPhoneme
+
+
+void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata)
+{//=====================================================
+// Examine the program of a single isolated phoneme
+ int ix;
+ PHONEME_LIST plist[4];
+ memset(plist, 0, sizeof(plist));
+
+ for(ix=0; ix<4; ix++)
+ {
+ plist[ix].phcode = phonPAUSE;
+ plist[ix].ph = phoneme_tab[phonPAUSE];
+ }
+
+ plist[1].phcode = phcode;
+ plist[1].ph = phoneme_tab[phcode];
+ plist[2].sourceix = 1;
+
+ InterpretPhoneme(NULL, 0, &plist[1], phdata, NULL);
+} // end of InterpretPhoneme2
diff --git a/navit/support/espeak/synthdata.h b/navit/support/espeak/synthdata.h
new file mode 100644
index 000000000..052ef81b2
--- /dev/null
+++ b/navit/support/espeak/synthdata.h
@@ -0,0 +1,20 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2010 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+void FreePhData(void);
diff --git a/navit/support/espeak/synthesize.c b/navit/support/espeak/synthesize.c
index 3a48b1d72..d28634bfc 100755..100644
--- a/navit/support/espeak/synthesize.c
+++ b/navit/support/espeak/synthesize.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -40,7 +40,7 @@ static void SmoothSpect(void);
// list of phonemes in a clause
int n_phoneme_list=0;
-PHONEME_LIST phoneme_list[N_PHONEME_LIST];
+PHONEME_LIST phoneme_list[N_PHONEME_LIST+1];
int mbrola_delay;
char mbrola_name[20];
@@ -54,6 +54,7 @@ static int last_wcmdq;
static int pitch_length;
static int amp_length;
static int modn_flags;
+static int fmt_amplitude=0;
static int syllable_start;
static int syllable_end;
@@ -69,7 +70,7 @@ SOUND_ICON soundicon_tab[N_SOUNDICON_TAB];
#define VOWEL_FRONT_LENGTH 50
-#define long(x) ((long)(x))
+
// a dummy phoneme_list entry which looks like a pause
static PHONEME_LIST next_pause;
@@ -81,7 +82,7 @@ const char *WordToString(unsigned int word)
int ix;
static char buf[5];
- for(ix=0; ix<3; ix++)
+ for(ix=0; ix<4; ix++)
buf[ix] = word >> (ix*8);
buf[4] = 0;
return(buf);
@@ -135,13 +136,13 @@ static void EndPitch(int voice_break)
syllable_centre = -1;
memset(vowel_transition,0,sizeof(vowel_transition));
}
-} // end of Synthesize::EndPitch
+} // end of EndPitch
static void DoAmplitude(int amp, unsigned char *amp_env)
{//=====================================================
- long *q;
+ long64 *q;
last_amp_cmd = wcmdq_tail;
amp_length = 0; // total length of vowel with this amplitude envelope
@@ -149,24 +150,24 @@ static void DoAmplitude(int amp, unsigned char *amp_env)
q = wcmdq[wcmdq_tail];
q[0] = WCMD_AMPLITUDE;
q[1] = 0; // fill in later from amp_length
- q[2] = (long)amp_env;
+ q[2] = (long64)amp_env;
q[3] = amp;
WcmdqInc();
-} // end of Synthesize::DoAmplitude
+} // end of DoAmplitude
static void DoPitch(unsigned char *env, int pitch1, int pitch2)
{//============================================================
- long *q;
+ long64 *q;
EndPitch(0);
- if(pitch1 == 1024)
+ if(pitch1 == 255)
{
// pitch was not set
- pitch1 = 24;
- pitch2 = 33;
+ pitch1 = 55;
+ pitch2 = 76;
env = envelope_data[PITCHfall];
}
last_pitch_cmd = wcmdq_tail;
@@ -178,141 +179,272 @@ static void DoPitch(unsigned char *env, int pitch1, int pitch2)
q = wcmdq[wcmdq_tail];
q[0] = WCMD_PITCH;
q[1] = 0; // length, fill in later from pitch_length
- q[2] = (long)env;
+ q[2] = (long64)env;
q[3] = (pitch1 << 16) + pitch2;
WcmdqInc();
-} // end of Synthesize::DoPitch
+} // end of DoPitch
int PauseLength(int pause, int control)
{//====================================
- int len;
+ unsigned int len;
if(control == 0)
- len = (pause * speed.speed_factor1)/256;
+ {
+ if(pause >= 200)
+ len = (pause * speed.clause_pause_factor)/256;
+ else
+ len = (pause * speed.pause_factor)/256;
+ }
else
- len = (pause * speed.speed_factor2)/256;
+ len = (pause * speed.wav_factor)/256;
- if(len < 5) len = 5; // mS, limit the amount to which pauses can be shortened
+ if(len < speed.min_pause)
+ {
+ len = speed.min_pause; // mS, limit the amount to which pauses can be shortened
+ }
return(len);
}
static void DoPause(int length, int control)
{//=========================================
+// length in nominal mS
// control = 1, less shortening at fast speeds
- int len;
+ unsigned int len;
+ int srate2;
- len = PauseLength(length, control);
+ if(length == 0)
+ len = 0;
+ else
+ {
+ len = PauseLength(length, control);
- len = (len * samplerate) / 1000; // convert from mS to number of samples
+ if(len < 90000)
+ {
+ len = (len * samplerate) / 1000; // convert from mS to number of samples
+ }
+ else
+ {
+ srate2 = samplerate / 25; // avoid overflow
+ len = (len * srate2) / 40;
+ }
+ }
EndPitch(1);
wcmdq[wcmdq_tail][0] = WCMD_PAUSE;
wcmdq[wcmdq_tail][1] = len;
WcmdqInc();
last_frame = NULL;
-} // end of Synthesize::DoPause
+
+ if(fmt_amplitude != 0)
+ {
+ wcmdq[wcmdq_tail][0] = WCMD_FMT_AMPLITUDE;
+ wcmdq[wcmdq_tail][1] = fmt_amplitude = 0;
+ WcmdqInc();
+ }
+} // end of DoPause
extern int seq_len_adjust; // temporary fix to advance the start point for playing the wav sample
-static int DoSample2(int index, int which, int length_mod, int amp)
-{//================================================================
+static int DoSample2(int index, int which, int std_length, int control, int length_mod, int amp)
+{//=============================================================================================
int length;
- int length1;
- int format;
+ int wav_length;
+ int wav_scale;
int min_length;
- int start=0;
- long *q;
+ int x;
+ int len4;
+ long64 *q;
unsigned char *p;
index = index & 0x7fffff;
p = &wavefile_data[index];
- format = p[2];
- length1 = (p[1] * 256);
- length1 += p[0]; // length in bytes
+ wav_scale = p[2];
+ wav_length = (p[1] * 256);
+ wav_length += p[0]; // length in bytes
+
+ if(wav_length == 0)
+ return(0);
+
+ min_length = speed.min_sample_len;
- if(seq_len_adjust > 0)
+ if(wav_scale==0)
+ min_length *= 2; // 16 bit samples
+ else
{
- start = (seq_len_adjust * samplerate)/1000;
- if(format == 0)
- start *= 2;
- length1 -= start;
- index += start;
+ // increase consonant amplitude at high speeds, depending on the peak consonant amplitude
+// x = ((35 - wav_scale) * speed.loud_consonants);
+// if(x < 0) x = 0;
+// wav_scale = (wav_scale * (x+256))/256;
}
+ if(std_length > 0)
+ {
+ std_length = (std_length * samplerate)/1000;
+ if(wav_scale == 0)
+ std_length *= 2;
- if(length_mod > 0)
- length = (length1 * length_mod) / 256;
+ x = (min_length * std_length)/wav_length;
+ if(x > min_length)
+ min_length = x;
+ }
else
- length = length1;
+ {
+ // no length specified, use the length of the stored sound
+ std_length = wav_length;
+ }
+ if(length_mod > 0)
+ {
+ std_length = (std_length * length_mod)/256;
+ }
- length = (length * speed.speed_factor2)/256;
- min_length = speed.min_sample_len;
- if(format==0)
- min_length *= 2;
+ length = (std_length * speed.wav_factor)/256;
+
+ if(control & pd_DONTLENGTHEN)
+ {
+ // this option is used for Stops, with short noise bursts.
+ // Don't change their length much.
+ if(length > std_length)
+ {
+ // don't let length exceed std_length
+ length = std_length;
+ }
+ else
+ {
+ // reduce the reduction in length
+// length = (length + std_length)/2;
+ }
+ }
if(length < min_length)
length = min_length;
- if(length > length1)
- length = length1; // don't exceed wavefile length
- if(format==0)
- length /= 2; // 2 byte samples
+ if(wav_scale == 0)
+ {
+ // 16 bit samples
+ length /= 2;
+ wav_length /= 2;
+ }
+
+ if(amp < 0)
+ return(length);
+ len4 = wav_length / 4;
index += 4;
- if(amp >= 0)
+ if(which & 0x100)
{
+ // mix this with synthesised wave
last_wcmdq = wcmdq_tail;
q = wcmdq[wcmdq_tail];
- if(which & 0x100)
- q[0] = WCMD_WAVE2; // mix this with synthesised wave
- else
- q[0] = WCMD_WAVE;
+ q[0] = WCMD_WAVE2;
+ q[1] = length | (wav_length << 16); // length in samples
+ q[2] = (long64)(&wavefile_data[index]);
+ q[3] = wav_scale + (amp << 8);
+ WcmdqInc();
+ return(length);
+ }
+
+ if(length > wav_length)
+ {
+ x = len4*3;
+ length -= x;
+ }
+ else
+ {
+ x = length;
+ length = 0;
+ }
+
+ last_wcmdq = wcmdq_tail;
+ q = wcmdq[wcmdq_tail];
+ q[0] = WCMD_WAVE;
+ q[1] = x; // length in samples
+ q[2] = (long64)(&wavefile_data[index]);
+ q[3] = wav_scale + (amp << 8);
+ WcmdqInc();
+
+
+ while(length > len4*3)
+ {
+ x = len4;
+ if(wav_scale == 0)
+ x *= 2;
+
+ last_wcmdq = wcmdq_tail;
+ q = wcmdq[wcmdq_tail];
+ q[0] = WCMD_WAVE;
+ q[1] = len4*2; // length in samples
+ q[2] = (long64)(&wavefile_data[index+x]);
+ q[3] = wav_scale + (amp << 8);
+ WcmdqInc();
+
+ length -= len4*2;
+ }
+
+ if(length > 0)
+ {
+ x = wav_length - length;
+ if(wav_scale == 0)
+ x *= 2;
+ last_wcmdq = wcmdq_tail;
+ q = wcmdq[wcmdq_tail];
+ q[0] = WCMD_WAVE;
q[1] = length; // length in samples
- q[2] = long(&wavefile_data[index]);
- q[3] = format + (amp << 8);
+ q[2] = (long64)(&wavefile_data[index+x]);
+ q[3] = wav_scale + (amp << 8);
WcmdqInc();
}
+
return(length);
-} // end of Synthesize::DoSample2
+} // end of DoSample2
-int DoSample(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int length_mod, int amp)
-{//====================== ==========================================================
- int index;
- int match_level;
- int amp2;
- int result;
+int DoSample3(PHONEME_DATA *phdata, int length_mod, int amp)
+{//=========================================================
+ int amp2;
+ int len;
EndPitch(1);
- index = LookupSound(ph1,ph2,which & 0xff,&match_level,0);
- if((index & 0x800000) == 0)
- return(0); // not wavefile data
-
- amp2 = wavefile_amp;
- if(amp != 0)
- amp2 = (amp * wavefile_amp)/20;
if(amp == -1)
+ {
+ // just get the length, don't produce sound
amp2 = amp;
+ }
+ else
+ {
+ amp2 = phdata->sound_param[pd_WAV];
+ if(amp2 == 0)
+ amp2 = 100;
+ amp2 = (amp2 * 32)/100;
+ }
- result = DoSample2(index,which,length_mod,amp2);
+ seq_len_adjust=0;
+
+ if(phdata->sound_addr[pd_WAV] == 0)
+ {
+ len = 0;
+ }
+ else
+ {
+ len = DoSample2(phdata->sound_addr[pd_WAV], 2, phdata->pd_param[pd_LENGTHMOD]*2, phdata->pd_control, length_mod, amp2);
+ }
last_frame = NULL;
- return(result);
-} // end of Synthesize::DoSample
+ return(len);
+} // end of DoSample3
-static frame_t *AllocFrame()
+static frame_t *AllocFrame(void)
{//=========================
// Allocate a temporary spectrum frame for the wavegen queue. Use a pool which is big
// enough to use a round-robin without checks.
@@ -361,7 +493,7 @@ static void set_frame_rms(frame_t *fr, int new_rms)
}
return;
}
-
+
if(fr->rms == 0) return; // check for divide by zero
x = (new_rms * 64)/fr->rms;
if(x >= 200) x = 199;
@@ -385,7 +517,7 @@ static void formants_reduce_hf(frame_t *fr, int level)
if(voice->klattv[0])
return;
-
+
for(ix=2; ix < 8; ix++)
{
x = fr->fheight[ix] * level;
@@ -472,7 +604,7 @@ static void AdjustFormants(frame_t *fr, int target, int min, int max, int f1_adj
fr->ffreq[1] += x;
fr->ffreq[0] += x;
}
- formants_reduce_hf(fr,hf_reduce);
+ formants_reduce_hf(fr,hf_reduce);
}
@@ -535,7 +667,7 @@ static short vcolouring[N_VCOLOUR][5] = {
// fprintf(stderr,"FMT%d %3s %3d-%3d f1=%d f2=%4d %4d %4d f3=%4d %3d\n",
// which,WordToString(other_ph->mnemonic),len,rms,f1,f2,f2_min,f2_max,f3_adj,f3_amp);
- if(other_ph->mnemonic == '?')
+ if((other_ph != NULL) && (other_ph->mnemonic == '?'))
flags |= 8;
if(which == 1)
@@ -546,14 +678,15 @@ static short vcolouring[N_VCOLOUR][5] = {
seq[0].length = VOWEL_FRONT_LENGTH;
if(len > 0)
seq[0].length = len;
- seq[0].frflags |= FRFLAG_LEN_MOD; // reduce length modification
- fr->frflags |= FRFLAG_LEN_MOD;
+ seq[0].frflags |= FRFLAG_LEN_MOD2; // reduce length modification
+ fr->frflags |= FRFLAG_LEN_MOD2;
next_rms = seq[1].frame->rms;
if(voice->klattv[0])
{
- fr->klattp[KLATT_AV] = 53; // reduce the amplituide of the start of a vowel
+// fr->klattp[KLATT_AV] = 53; // reduce the amplituide of the start of a vowel
+ fr->klattp[KLATT_AV] = seq[1].frame->klattp[KLATT_AV] - 4;
}
if(f2 != 0)
{
@@ -594,7 +727,7 @@ if(voice->klattv[0])
fr = CopyFrame(seq[*n_frames-1].frame,0);
seq[*n_frames-1].frame = fr;
rms = RMS_GLOTTAL1;
-
+
// degree of glottal-stop effect depends on closeness of vowel (indicated by f1 freq)
modn_flags = 0x400 + (VowelCloseness(fr) << 8);
}
@@ -603,7 +736,7 @@ if(voice->klattv[0])
fr = DuplicateLastFrame(seq,(*n_frames)++,len);
if(len > 36)
seq_len_adjust += (len - 36);
-
+
if(f2 != 0)
{
AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags);
@@ -614,11 +747,11 @@ if(voice->klattv[0])
if((vcolour > 0) && (vcolour <= N_VCOLOUR))
{
- for(ix=0; ix<*n_frames; ix++)
+ for(ix=0; ix < *n_frames; ix++)
{
fr = CopyFrame(seq[ix].frame,0);
seq[ix].frame = fr;
-
+
for(formant=1; formant<=5; formant++)
{
int x;
@@ -639,7 +772,7 @@ if(voice->klattv[0])
}
if(flags & 0x40)
- DoPause(12,0); // add a short pause after the consonant
+ DoPause(20,0); // add a short pause after the consonant
if(flags & 16)
return(len);
@@ -652,7 +785,7 @@ static void SmoothSpect(void)
{//==========================
// Limit the rate of frequence change of formants, to reduce chirping
- long *q;
+ long64 *q;
frame_t *frame;
frame_t *frame2;
frame_t *frame1;
@@ -694,7 +827,7 @@ static void SmoothSpect(void)
frame1 = (frame_t *)q[3];
if(frame1 == frame)
{
- q[3] = (long)frame2;
+ q[3] = (long64)frame2;
frame1 = frame2;
}
else
@@ -742,7 +875,7 @@ static void SmoothSpect(void)
modified = 1;
}
frame2->ffreq[pk] = frame1->ffreq[pk] + allowed;
- q[2] = (long)frame2;
+ q[2] = (long64)frame2;
}
else
if(diff < -allowed)
@@ -753,7 +886,7 @@ static void SmoothSpect(void)
modified = 1;
}
frame2->ffreq[pk] = frame1->ffreq[pk] - allowed;
- q[2] = (long)frame2;
+ q[2] = (long64)frame2;
}
}
}
@@ -784,7 +917,7 @@ static void SmoothSpect(void)
{
if(frame1 == frame)
{
- q[2] = (long)frame2;
+ q[2] = (long64)frame2;
frame1 = frame2;
}
else
@@ -826,7 +959,7 @@ static void SmoothSpect(void)
modified = 1;
}
frame2->ffreq[pk] = frame1->ffreq[pk] + allowed;
- q[3] = (long)frame2;
+ q[3] = (long64)frame2;
}
else
if(diff < -allowed)
@@ -837,7 +970,7 @@ static void SmoothSpect(void)
modified = 1;
}
frame2->ffreq[pk] = frame1->ffreq[pk] - allowed;
- q[3] = (long)frame2;
+ q[3] = (long64)frame2;
}
}
}
@@ -860,10 +993,10 @@ static void StartSyllable(void)
}
-int DoSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
- int which, PHONEME_LIST *plist, int modulation)
-{//===================================================================================
- // which 1 start of phoneme, 2 body and end
+
+int DoSpect2(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, PHONEME_LIST *plist, int modulation)
+{//========================================================================================================
+ // which: 0 not a vowel, 1 start of vowel, 2 body and end of vowel
// length_mod: 256 = 100%
// modulation: -1 = don't write to wcmdq
@@ -874,25 +1007,35 @@ int DoSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
frame_t *frame2;
frame_t *fr;
int ix;
- long *q;
+ long64 *q;
int len;
- int match_level;
int frame_length;
- int frame1_length;
- int frame2_length;
int length_factor;
int length_mod;
+ int length_sum;
+ int length_min;
int total_len = 0;
static int wave_flag = 0;
int wcmd_spect = WCMD_SPECT;
+ int frame_lengths[N_SEQ_FRAMES];
+
+ if(fmt_params->fmt_addr == 0)
+ return(0);
length_mod = plist->length;
if(length_mod==0) length_mod=256;
+ length_min = (samplerate/70); // greater than one cycle at low pitch (Hz)
+ if(which==2)
+ {
+ if((translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD] > 0) && ((this_ph->std_length >= translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD]) || (plist->synthflags & SFLAG_LENGTHEN) || (this_ph->phflags & phLONG)))
+ length_min *= 2; // ensure long vowels are longer
+ }
+
if(which==1)
{
// limit the shortening of sonorants before shortened (eg. unstressed vowels)
- if((this_ph->type==phLIQUID) || (prev_ph->type==phLIQUID) || (prev_ph->type==phNASAL))
+ if((this_ph->type==phLIQUID) || (plist[-1].type==phLIQUID) || (plist[-1].type==phNASAL))
{
if(length_mod < (len = translator->langopts.param[LOPT_SONORANT_MIN]))
{
@@ -902,15 +1045,30 @@ if(which==1)
}
modn_flags = 0;
- frames = LookupSpect(this_ph,prev_ph,next_ph,which,&match_level,&n_frames, plist);
+ frames = LookupSpect(this_ph, which, fmt_params, &n_frames, plist);
if(frames == NULL)
return(0); // not found
+ if(fmt_params->fmt_amp != fmt_amplitude)
+ {
+ // an amplitude adjustment is specified for this sequence
+ q = wcmdq[wcmdq_tail];
+ q[0] = WCMD_FMT_AMPLITUDE;
+ q[1] = fmt_amplitude = fmt_params->fmt_amp;
+ WcmdqInc();
+ }
+
frame1 = frames[0].frame;
- frame1_length = frames[0].length;
if(voice->klattv[0])
wcmd_spect = WCMD_KLATT;
+ wavefile_ix = fmt_params->wav_addr;
+
+ if(fmt_params->wav_amp == 0)
+ wavefile_amp = 32;
+ else
+ wavefile_amp = (fmt_params->wav_amp * 32)/100;
+
if(wavefile_ix == 0)
{
if(wave_flag)
@@ -935,7 +1093,7 @@ if(which==1)
&& !(last_frame->frflags & FRFLAG_BREAK))
{
// last frame of previous sequence was zero-length, replace with first of this sequence
- wcmdq[last_wcmdq][3] = (long)frame1;
+ wcmdq[last_wcmdq][3] = (long64)frame1;
if(last_frame->frflags & FRFLAG_BREAK_LF)
{
@@ -947,7 +1105,7 @@ if(which==1)
fr->ffreq[ix] = last_frame->ffreq[ix];
fr->fheight[ix] = last_frame->fheight[ix];
}
- wcmdq[last_wcmdq][3] = (long)fr;
+ wcmdq[last_wcmdq][3] = (long64)fr;
}
}
}
@@ -960,29 +1118,50 @@ if(which==1)
syllable_centre = wcmdq_tail;
}
- frame_length = frame1_length;
+ length_sum = 0;
+ for(frameix=1; frameix < n_frames; frameix++)
+ {
+ length_factor = length_mod;
+ if(frames[frameix-1].frflags & FRFLAG_LEN_MOD) // reduce effect of length mod
+ {
+ length_factor = (length_mod*(256-speed.lenmod_factor) + 256*speed.lenmod_factor)/256;
+ }
+ else
+ if(frames[frameix-1].frflags & FRFLAG_LEN_MOD2) // reduce effect of length mod, used for the start of a vowel
+ {
+ length_factor = (length_mod*(256-speed.lenmod2_factor) + 256*speed.lenmod2_factor)/256;
+ }
+
+ frame_length = frames[frameix-1].length;
+ len = (frame_length * samplerate)/1000;
+ len = (len * length_factor)/256;
+ length_sum += len;
+ frame_lengths[frameix] = len;
+ }
+
+ if((length_sum > 0) && (length_sum < length_min))
+ {
+ // lengthen, so that the sequence is greater than one cycle at low pitch
+ for(frameix=1; frameix < n_frames; frameix++)
+ {
+ frame_lengths[frameix] = (frame_lengths[frameix] * length_min) / length_sum;
+ }
+ }
+
for(frameix=1; frameix<n_frames; frameix++)
{
frame2 = frames[frameix].frame;
- frame2_length = frames[frameix].length;
- if((wavefile_ix != 0) && ((frame1->frflags & FRFLAG_DEFER_WAV)==0))
+ if((fmt_params->wav_addr != 0) && ((frame1->frflags & FRFLAG_DEFER_WAV)==0))
{
// there is a wave file to play along with this synthesis
seq_len_adjust = 0;
- DoSample2(wavefile_ix,which+0x100,0,wavefile_amp);
+ DoSample2(fmt_params->wav_addr, which+0x100, 0, fmt_params->fmt_control, 0, wavefile_amp);
wave_flag = 1;
wavefile_ix = 0;
+ fmt_params->wav_addr = 0;
}
- length_factor = length_mod;
- if(frame1->frflags & FRFLAG_LEN_MOD) // reduce effect of length mod
- {
- length_factor = (length_mod*(256-speed.speed_factor3) + 256*speed.speed_factor3)/256;
- }
- len = (frame_length * samplerate)/1000;
- len = (len * length_factor)/256;
-
if(modulation >= 0)
{
if(frame1->frflags & FRFLAG_MODULATE)
@@ -993,13 +1172,13 @@ if(which==1)
modulation |= modn_flags; // before or after a glottal stop
}
+ len = frame_lengths[frameix];
pitch_length += len;
amp_length += len;
- if(frame_length < 2)
+ if(len == 0)
{
last_frame = NULL;
- frame_length = frame2_length;
frame1 = frame2;
}
else
@@ -1011,31 +1190,72 @@ if(which==1)
q = wcmdq[wcmdq_tail];
q[0] = wcmd_spect;
q[1] = len + (modulation << 16);
- q[2] = long(frame1);
- q[3] = long(frame2);
-
+ q[2] = (long64)frame1;
+ q[3] = (long64)frame2;
+
WcmdqInc();
}
last_frame = frame1 = frame2;
- frame_length = frame2_length;
total_len += len;
}
}
+
+ if((which != 1) && (fmt_amplitude != 0))
+ {
+ q = wcmdq[wcmdq_tail];
+ q[0] = WCMD_FMT_AMPLITUDE;
+ q[1] = fmt_amplitude = 0;
+ WcmdqInc();
+ }
+
+
return(total_len);
-} // end of Synthesize::DoSpect
+} // end of DoSpect
-static void DoMarker(int type, int char_posn, int length, int value)
-{//=================================================================
+
+
+void DoMarker(int type, int char_posn, int length, int value)
+{//==========================================================
// This could be used to return an index to the word currently being spoken
// Type 1=word, 2=sentence, 3=named marker, 4=play audio, 5=end
- wcmdq[wcmdq_tail][0] = WCMD_MARKER;
- wcmdq[wcmdq_tail][1] = type;
- wcmdq[wcmdq_tail][2] = (char_posn & 0xffffff) | (length << 24);
- wcmdq[wcmdq_tail][3] = value;
- WcmdqInc();
+ if(WcmdqFree() > 5)
+ {
+ wcmdq[wcmdq_tail][0] = WCMD_MARKER + (type << 8);
+ wcmdq[wcmdq_tail][1] = (char_posn & 0xffffff) | (length << 24);
+ wcmdq[wcmdq_tail][2] = value;
+ WcmdqInc();
+ }
+} // end of DoMarker
+
+
+void DoPhonemeMarker(int type, int char_posn, int length, char *name)
+{//==================================================================
+// This could be used to return an index to the word currently being spoken
+// Type 7=phoneme
+ int *p;
+
+ if(WcmdqFree() > 5)
+ {
+ wcmdq[wcmdq_tail][0] = WCMD_MARKER + (type << 8);
+ wcmdq[wcmdq_tail][1] = (char_posn & 0xffffff) | (length << 24);
+ p = (int *)name;
+ wcmdq[wcmdq_tail][2] = p[0]; // up to 8 bytes of UTF8 characters
+ wcmdq[wcmdq_tail][3] = p[1];
+ WcmdqInc();
+ }
+} // end of DoMarker
+
-} // end of Synthesize::DoMarker
+#ifdef INCLUDE_SONIC
+void DoSonicSpeed(int value)
+{//=========================
+// value, multiplier * 1024
+ wcmdq[wcmdq_tail][0] = WCMD_SONIC_SPEED;
+ wcmdq[wcmdq_tail][1] = value;
+ WcmdqInc();
+} // end of DoSonicSpeed
+#endif
void DoVoiceChange(voice_t *v)
@@ -1046,23 +1266,28 @@ void DoVoiceChange(voice_t *v)
v2 = (voice_t *)malloc(sizeof(voice_t));
memcpy(v2,v,sizeof(voice_t));
wcmdq[wcmdq_tail][0] = WCMD_VOICE;
- wcmdq[wcmdq_tail][1] = (long)(v2);
+ wcmdq[wcmdq_tail][2] = (long64)v2;
WcmdqInc();
}
-static void DoEmbedded(int *embix, int sourceix)
-{//=============================================
+void DoEmbedded(int *embix, int sourceix)
+{//======================================
// There were embedded commands in the text at this point
unsigned int word; // bit 7=last command for this word, bits 5,6 sign, bits 0-4 command
unsigned int value;
int command;
do {
- word = embedded_list[(*embix)++];
+ word = embedded_list[*embix];
value = word >> 8;
command = word & 0x7f;
+ if(command == 0)
+ return; // error
+
+ (*embix)++;
+
switch(command & 0x1f)
{
case EMBED_S: // speed
@@ -1078,7 +1303,7 @@ static void DoEmbedded(int *embix, int sourceix)
DoPause(10,0); // ensure a break in the speech
wcmdq[wcmdq_tail][0] = WCMD_WAVE;
wcmdq[wcmdq_tail][1] = soundicon_tab[value].length;
- wcmdq[wcmdq_tail][2] = (long)soundicon_tab[value].data + 44; // skip WAV header
+ wcmdq[wcmdq_tail][2] = (long64)soundicon_tab[value].data + 44; // skip WAV header
wcmdq[wcmdq_tail][3] = 0x1500; // 16 bit data, amp=21
WcmdqInc();
}
@@ -1123,17 +1348,27 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
unsigned char *pitch_env=NULL;
unsigned char *amp_env;
PHONEME_TAB *ph;
- PHONEME_TAB *prev_ph;
+ int use_ipa=0;
+ int done_phoneme_marker;
+ char phoneme_name[16];
static int sourceix=0;
-#ifdef TEST_MBROLA
- if(mbrola_name[0] != 0)
- return(MbrolaGenerate(phoneme_list,n_ph,resume));
-#endif
+ PHONEME_DATA phdata;
+ PHONEME_DATA phdata_prev;
+ PHONEME_DATA phdata_next;
+ PHONEME_DATA phdata_tone;
+ FMT_PARAMS fmtp;
+ static WORD_PH_DATA worddata;
if(option_quiet)
return(0);
+ if(option_phoneme_events & espeakINITIALIZE_PHONEME_IPA)
+ use_ipa = 1;
+
+ if(mbrola_name[0] != 0)
+ return(MbrolaGenerate(phoneme_list,n_ph,resume));
+
if(resume == 0)
{
ix = 1;
@@ -1148,19 +1383,21 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
syllable_centre = -1;
last_pitch_cmd = -1;
memset(vowel_transition,0,sizeof(vowel_transition));
+ memset(&worddata, 0, sizeof(worddata));
+ DoPause(0,0); // isolate from the previous clause
}
- while(ix < (*n_ph))
+ while((ix < (*n_ph)) && (ix < N_PHONEME_LIST-2))
{
p = &phoneme_list[ix];
if(p->type == phPAUSE)
- free_min = 5;
+ free_min = 10;
else
if(p->type != phVOWEL)
- free_min = 10; // we need less Q space for non-vowels, and we need to generate phonemes after a vowel so that the pitch_length is filled in
+ free_min = 15; // we need less Q space for non-vowels, and we need to generate phonemes after a vowel so that the pitch_length is filled in
else
- free_min = MIN_WCMDQ; // 22
+ free_min = MIN_WCMDQ; // 25
if(WcmdqFree() <= free_min)
return(1); // wait
@@ -1176,8 +1413,14 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
if(p->newword)
{
- if(translator->langopts.param[LOPT_WORD_MERGE] == 0)
+ if(((p->type == phVOWEL) && (translator->langopts.param[LOPT_WORD_MERGE] & 1)) ||
+ (p->ph->phflags & phNOPAUSE))
+ {
+ }
+ else
+ {
last_frame = NULL;
+ }
sourceix = (p->sourceix & 0x7ff) + clause_start_char;
@@ -1188,44 +1431,91 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
// DoMarker(espeakEVENT_END, count_characters, 0, count_sentences); // end of clause
if(p->newword & 1)
- DoMarker(espeakEVENT_WORD, sourceix, p->sourceix >> 11, clause_start_word + word_count++);
+ DoMarker(espeakEVENT_WORD, sourceix, p->sourceix >> 11, clause_start_word + word_count++); // NOTE, this count doesn't include multiple-word pronunciations in *_list. eg (of a)
}
EndAmplitude();
- if(p->prepause > 0)
+ if((p->prepause > 0) && !(p->ph->phflags & phPREVOICE))
DoPause(p->prepause,1);
- if(option_phoneme_events && (p->type != phVOWEL))
+ done_phoneme_marker = 0;
+ if(option_phoneme_events && (p->ph->code != phonEND_WORD))
{
- // Note, for vowels, do the phoneme event after the vowel-start
- DoMarker(espeakEVENT_PHONEME, sourceix, 0, p->ph->mnemonic);
+ if((p->type == phVOWEL) && (prev->type==phLIQUID || prev->type==phNASAL))
+ {
+ // For vowels following a liquid or nasal, do the phoneme event after the vowel-start
+ }
+ else
+ {
+ WritePhMnemonic(phoneme_name, p->ph, p, use_ipa, NULL);
+ DoPhonemeMarker(espeakEVENT_PHONEME, sourceix, 0, phoneme_name);
+ done_phoneme_marker = 1;
+ }
}
switch(p->type)
{
case phPAUSE:
DoPause(p->length,0);
+#ifdef _ESPEAKEDIT
+ p->std_length = p->ph->std_length;
+#endif
break;
case phSTOP:
released = 0;
- if(next->type==phVOWEL) released = 1;
- if(next->type==phLIQUID && !next->newword) released = 1;
-
- if(released)
- DoSample(p->ph,next->ph,2,0,0);
+ ph = p->ph;
+ if(next->type==phVOWEL)
+ {
+ released = 1;
+ }
else
- DoSample(p->ph,phoneme_tab[phonPAUSE],2,0,0);
+ if(!next->newword)
+ {
+ if(next->type==phLIQUID) released = 1;
+// if(((p->ph->phflags & phPLACE) == phPLACE_blb) && (next->ph->phflags & phSIBILANT)) released = 1;
+ }
+ if(released == 0)
+ p->synthflags |= SFLAG_NEXT_PAUSE;
+
+ if(ph->phflags & phPREVOICE)
+ {
+ // a period of voicing before the release
+ memset(&fmtp, 0, sizeof(fmtp));
+ InterpretPhoneme(NULL, 0x01, p, &phdata, &worddata);
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ fmtp.fmt_amp = phdata.sound_param[pd_FMT];
+
+ if(last_pitch_cmd < 0)
+ {
+ DoAmplitude(next->amp,NULL);
+ DoPitch(envelope_data[p->env],next->pitch1,next->pitch2);
+ }
+
+ DoSpect2(ph, 0, &fmtp, p, 0);
+ }
+
+ InterpretPhoneme(NULL, 0, p, &phdata, &worddata);
+ phdata.pd_control |= pd_DONTLENGTHEN;
+ DoSample3(&phdata, 0, 0);
break;
case phFRICATIVE:
+ InterpretPhoneme(NULL, 0, p, &phdata, &worddata);
+
if(p->synthflags & SFLAG_LENGTHEN)
- DoSample(p->ph,next->ph,2,p->length,0); // play it twice for [s:] etc.
- DoSample(p->ph,next->ph,2,p->length,0);
+ {
+ DoSample3(&phdata, p->length, 0); // play it twice for [s:] etc.
+ }
+ DoSample3(&phdata, p->length, 0);
break;
case phVSTOP:
+ ph = p->ph;
+ memset(&fmtp, 0, sizeof(fmtp));
+ fmtp.fmt_control = pd_DONTLENGTHEN;
+
pre_voiced = 0;
if(next->type==phVOWEL)
{
@@ -1249,14 +1539,18 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
}
}
- if((prev->type==phVOWEL) || (prev->ph->phflags & phVOWEL2))
+ if((prev->type==phVOWEL) || (prev->ph->phflags & phVOWEL2) || (ph->phflags & phPREVOICE))
{
// a period of voicing before the release
- DoSpect(p->ph,phoneme_tab[phonSCHWA],next->ph,1,p,0);
+ InterpretPhoneme(NULL, 0x01, p, &phdata, &worddata);
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ fmtp.fmt_amp = phdata.sound_param[pd_FMT];
+
+ DoSpect2(ph, 0, &fmtp, p, 0);
if(p->synthflags & SFLAG_LENGTHEN)
{
- DoPause(20,0);
- DoSpect(p->ph,phoneme_tab[phonSCHWA],next->ph,1,p,0);
+ DoPause(25,1);
+ DoSpect2(ph, 0, &fmtp, p, 0);
}
}
else
@@ -1271,15 +1565,24 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
{
// followed by a vowel, or liquid + vowel
StartSyllable();
- DoSpect(p->ph,prev->ph,next->ph,2,p,0);
}
else
{
-// if((prev->type != phVOWEL) && ((prev->ph->phflags & phVOICED)==0) && ((next->ph->phflags & phVOICED)==0))
-// DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE_SHORT],2,p,0);
-// else
- DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
-// DoSpect(p->ph,prev->ph,next->ph,2,p,0);
+ p->synthflags |= SFLAG_NEXT_PAUSE;
+ }
+ InterpretPhoneme(NULL,0, p, &phdata, &worddata);
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ fmtp.fmt_amp = phdata.sound_param[pd_FMT];
+ fmtp.wav_addr = phdata.sound_addr[pd_ADDWAV];
+ fmtp.wav_amp = phdata.sound_param[pd_ADDWAV];
+ DoSpect2(ph, 0, &fmtp, p, 0);
+
+ if((p->newword == 0) && (next2->newword == 0))
+ {
+ if(next->type == phVFRICATIVE)
+ DoPause(20,0);
+ if(next->type == phFRICATIVE)
+ DoPause(12,0);
}
break;
@@ -1304,22 +1607,29 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
}
}
- if((next->type==phVOWEL) || ((next->type==phLIQUID)) && (next->newword==0)) // ?? test 14.Aug.2007
+ if((next->type==phVOWEL) || ((next->type==phLIQUID) && (next->newword==0))) // ?? test 14.Aug.2007
{
StartSyllable();
- if(p->synthflags & SFLAG_LENGTHEN)
- DoSpect(p->ph,prev->ph,next->ph,2,p,0);
- DoSpect(p->ph,prev->ph,next->ph,2,p,0);
}
else
{
- if(p->synthflags & SFLAG_LENGTHEN)
- DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
- DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
+ p->synthflags |= SFLAG_NEXT_PAUSE;
}
+ InterpretPhoneme(NULL,0, p, &phdata, &worddata);
+ memset(&fmtp, 0, sizeof(fmtp));
+ fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2;
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ fmtp.fmt_amp = phdata.sound_param[pd_FMT];
+ fmtp.wav_addr = phdata.sound_addr[pd_ADDWAV];
+ fmtp.wav_amp = phdata.sound_param[pd_ADDWAV];
+
+ if(p->synthflags & SFLAG_LENGTHEN)
+ DoSpect2(p->ph, 0, &fmtp, p, 0);
+ DoSpect2(p->ph, 0, &fmtp, p, 0);
break;
case phNASAL:
+ memset(&fmtp, 0, sizeof(fmtp));
if(!(p->synthflags & SFLAG_SEQCONTINUE))
{
DoAmplitude(p->amp,NULL);
@@ -1331,37 +1641,36 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
last_frame = NULL;
}
+ InterpretPhoneme(NULL,0, p, &phdata, &worddata);
+ fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2;
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ fmtp.fmt_amp = phdata.sound_param[pd_FMT];
+
if(next->type==phVOWEL)
{
StartSyllable();
- DoSpect(p->ph,prev->ph,next->ph,1,p,0);
+ DoSpect2(p->ph, 0, &fmtp, p, 0);
}
else
if(prev->type==phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE))
{
- DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
+ DoSpect2(p->ph, 0, &fmtp, p, 0);
}
else
{
last_frame = NULL; // only for nasal ?
- if(next->type == phLIQUID)
- DoSpect(p->ph,prev->ph,phoneme_tab[phonSONORANT],2,p,0);
- else
- DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,0);
+ DoSpect2(p->ph, 0, &fmtp, p, 0);
last_frame = NULL;
}
break;
case phLIQUID:
+ memset(&fmtp, 0, sizeof(fmtp));
modulation = 0;
if(p->ph->phflags & phTRILL)
modulation = 5;
- prev_ph = prev->ph;
-// if(p->newword)
-// prev_ph = phoneme_tab[phonPAUSE]; // pronounce fully at the start of a word
-
if(!(p->synthflags & SFLAG_SEQCONTINUE))
{
DoAmplitude(p->amp,NULL);
@@ -1376,17 +1685,14 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
if(next->type==phVOWEL)
{
StartSyllable();
- DoSpect(p->ph,prev_ph,next->ph,1,p,modulation); // (,)r
- }
- else
- if(prev->type==phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE))
- {
- DoSpect(p->ph,prev_ph,next->ph,1,p,modulation);
- }
- else
- {
- DoSpect(p->ph,prev_ph,next->ph,1,p,modulation);
}
+ InterpretPhoneme(NULL, 0, p, &phdata, &worddata);
+ fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2;
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ fmtp.fmt_amp = phdata.sound_param[pd_FMT];
+ fmtp.wav_addr = phdata.sound_addr[pd_ADDWAV];
+ fmtp.wav_amp = phdata.sound_param[pd_ADDWAV];
+ DoSpect2(p->ph, 0, &fmtp, p, modulation);
break;
@@ -1394,16 +1700,48 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
ph = p->ph;
stress = p->stresslevel & 0xf;
- // vowel transition from the preceding phoneme
- vowel_transition0 = vowel_transition[0];
- vowel_transition1 = vowel_transition[1];
+ memset(&fmtp, 0, sizeof(fmtp));
+
+ InterpretPhoneme(NULL, 0, p, &phdata, &worddata);
+ fmtp.std_length = phdata.pd_param[i_SET_LENGTH] * 2;
+
+ if(((fmtp.fmt_addr = phdata.sound_addr[pd_VWLSTART]) != 0) && ((phdata.pd_control & pd_FORNEXTPH) == 0))
+ {
+ // a vowel start has been specified by the Vowel program
+ fmtp.fmt_length = phdata.sound_param[pd_VWLSTART];
+ }
+ else
+ if(prev->type != phPAUSE)
+ {
+ // check the previous phoneme
+ InterpretPhoneme(NULL, 0, prev, &phdata_prev, NULL);
+ if((fmtp.fmt_addr = phdata_prev.sound_addr[pd_VWLSTART]) != 0)
+ {
+ // a vowel start has been specified by the Vowel program
+ fmtp.fmt2_lenadj = phdata_prev.sound_param[pd_VWLSTART];
+ }
+ fmtp.transition0 = phdata_prev.vowel_transition[0];
+ fmtp.transition1 = phdata_prev.vowel_transition[1];
+ }
+
+ if(fmtp.fmt_addr == 0)
+ {
+ // use the default start for this vowel
+ fmtp.use_vowelin = 1;
+ fmtp.fmt_control = 1;
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ }
+
+ fmtp.fmt_amp = phdata.sound_param[pd_FMT];
pitch_env = envelope_data[p->env];
amp_env = NULL;
if(p->tone_ph != 0)
{
- pitch_env = LookupEnvelope(phoneme_tab[p->tone_ph]->spect);
- amp_env = LookupEnvelope(phoneme_tab[p->tone_ph]->after);
+ InterpretPhoneme2(p->tone_ph, &phdata_tone);
+ pitch_env = GetEnvelope(phdata_tone.pitch_env);
+ if(phdata_tone.amp_env > 0)
+ amp_env = GetEnvelope(phdata_tone.amp_env);
}
StartSyllable();
@@ -1419,13 +1757,13 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
{
DoAmplitude(p->amp,amp_env);
DoPitch(pitch_env,p->pitch1,p->pitch2); // don't use prevocalic rising tone
- DoSpect(ph,prev->ph,next->ph,1,p,modulation);
+ DoSpect2(ph, 1, &fmtp, p, modulation);
}
else
if(prev->type==phLIQUID || prev->type==phNASAL)
{
DoAmplitude(p->amp,amp_env);
- DoSpect(ph,prev->ph,next->ph,1,p,modulation); // continue with pre-vocalic rising tone
+ DoSpect2(ph, 1, &fmtp, p, modulation); // continue with pre-vocalic rising tone
DoPitch(pitch_env,p->pitch1,p->pitch2);
}
else
@@ -1436,17 +1774,42 @@ int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
DoPitch(pitch_env,p->pitch1,p->pitch2);
}
- DoSpect(ph,prev->ph,next->ph,1,p,modulation);
+ DoSpect2(ph, 1, &fmtp, p, modulation);
+ }
+
+ if((option_phoneme_events) && (done_phoneme_marker == 0))
+ {
+ WritePhMnemonic(phoneme_name, p->ph, p, use_ipa, NULL);
+ DoPhonemeMarker(espeakEVENT_PHONEME, sourceix, 0, phoneme_name);
}
- if(option_phoneme_events)
+ fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
+ fmtp.fmt_amp = phdata.sound_param[pd_FMT];
+ fmtp.transition0 = 0;
+ fmtp.transition1 = 0;
+
+ if((fmtp.fmt2_addr = phdata.sound_addr[pd_VWLEND]) != 0)
{
- DoMarker(espeakEVENT_PHONEME, sourceix, 0, p->ph->mnemonic);
+ fmtp.fmt2_lenadj = phdata.sound_param[pd_VWLEND];
}
+ else
+ if(next->type != phPAUSE)
+ {
+ fmtp.fmt2_lenadj = 0;
+ InterpretPhoneme(NULL, 0, next, &phdata_next, NULL);
- DoSpect(p->ph,prev->ph,next->ph,2,p,modulation);
+ fmtp.use_vowelin = 1;
+ fmtp.transition0 = phdata_next.vowel_transition[2]; // always do vowel_transition, even if ph_VWLEND ?? consider [N]
+ fmtp.transition1 = phdata_next.vowel_transition[3];
+
+ if((fmtp.fmt2_addr = phdata_next.sound_addr[pd_VWLEND]) != 0)
+ {
+ fmtp.fmt2_lenadj = phdata_next.sound_param[pd_VWLEND];
+ }
+ }
+
+ DoSpect2(ph, 2, &fmtp, p, modulation);
- memset(vowel_transition,0,sizeof(vowel_transition));
break;
}
ix++;
@@ -1512,6 +1875,7 @@ int SpeakNextClause(FILE *f_in, const void *text_in, int control)
char *voice_change;
static FILE *f_text=NULL;
static const void *p_text=NULL;
+ const char *phon_out;
if(control == 4)
{
@@ -1534,7 +1898,6 @@ int SpeakNextClause(FILE *f_in, const void *text_in, int control)
n_phoneme_list = 0;
WcmdqStop();
- embedded_value[EMBED_T] = 0;
return(0);
}
@@ -1599,20 +1962,21 @@ int SpeakNextClause(FILE *f_in, const void *text_in, int control)
CalcPitches(translator, clause_tone);
CalcLengths(translator);
- GetTranslatedPhonemeString(translator->phon_out,sizeof(translator->phon_out));
- if(option_phonemes > 0)
+ if((option_phonemes > 0) || (phoneme_callback != NULL))
{
- fprintf(f_trans,"%s\n",translator->phon_out);
+ int phoneme_mode = 0;
+ if(option_phonemes >= 3)
+ phoneme_mode = 0x10 + option_phonemes-3; // 0x10=ipa, 0x11=ipa with tie, 0x12=ipa with ZWJ, 0x13=ipa with separators
- if(!iswalpha(0x010d))
+ phon_out = GetTranslatedPhonemeString(phoneme_mode);
+ if(option_phonemes > 0)
{
- // check that c-caron is recognized as an alphabetic character
- fprintf(stderr,"Warning: Accented letters are not recognized, eg: U+010D\nSet LC_CTYPE to a UTF-8 locale\n");
+ fprintf(f_trans,"%s\n",phon_out);
+ }
+ if(phoneme_callback != NULL)
+ {
+ phoneme_callback(phon_out);
}
- }
- if(phoneme_callback != NULL)
- {
- phoneme_callback(translator->phon_out);
}
@@ -1622,20 +1986,6 @@ int SpeakNextClause(FILE *f_in, const void *text_in, int control)
return(1);
}
- if(mbrola_name[0] != 0)
- {
-#ifdef USE_MBROLA_LIB
- MbrolaTranslate(phoneme_list,n_phoneme_list,NULL);
-#else
- {
- FILE *f_mbrola;
- if((f_mbrola = f_trans) == stderr)
- f_mbrola = stdout;
- MbrolaTranslate(phoneme_list,n_phoneme_list,f_mbrola);
- }
-#endif
- }
-
Generate(phoneme_list,&n_phoneme_list,0);
WavegenOpenSound();
diff --git a/navit/support/espeak/synthesize.h b/navit/support/espeak/synthesize.h
index 193b93297..7bc234c8d 100755..100644
--- a/navit/support/espeak/synthesize.h
+++ b/navit/support/espeak/synthesize.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -17,6 +17,8 @@
* <http://www.gnu.org/licenses/>. *
***************************************************************************/
+#define espeakINITIALIZE_PHONEME_IPA 0x0002 // move this to speak_lib.h, after eSpeak version 1.46.02
+
#define N_PHONEME_LIST 1000 // enough for source[N_TR_SOURCE] full of text, else it will truncate
@@ -24,9 +26,6 @@
#define N_SEQ_FRAMES 25 // max frames in a spectrum sequence (real max is ablut 8)
#define STEPSIZE 64 // 2.9mS at 22 kHz sample rate
-#define PITCHfall 0
-#define PITCHrise 1
-
// flags set for frames within a spectrum sequence
#define FRFLAG_KLATT 0x01 // this frame includes extra data for Klatt synthesizer
#define FRFLAG_VOWEL_CENTRE 0x02 // centre point of vowel
@@ -37,6 +36,7 @@
#define FRFLAG_FORMANT_RATE 0x20 // Flag5 allow increased rate of change of formant freq
#define FRFLAG_MODULATE 0x40 // Flag6 modulate amplitude of some cycles to give trill
#define FRFLAG_DEFER_WAV 0x80 // Flag7 defer mixing WAV until the next frame
+#define FRFLAG_LEN_MOD2 0x4000 // reduce effect of length adjustment, used for the start of a vowel
#define FRFLAG_COPIED 0x8000 // This frame has been copied into temporary rw memory
#define SFLAG_SEQCONTINUE 0x01 // a liquid or nasal after a vowel, but not followed by a vowel
@@ -47,13 +47,16 @@
#define SFLAG_SWITCHED_LANG 0x20 // this word uses phonemes from a different language
#define SFLAG_PROMOTE_STRESS 0x40 // this unstressed word can be promoted to stressed
+#define SFLAG_PREV_PAUSE 0x1000 // consider previous phoneme as pause
+#define SFLAG_NEXT_PAUSE 0x2000 // consider next phoneme as pause
+
// embedded command numbers
#define EMBED_P 1 // pitch
#define EMBED_S 2 // speed (used in setlengths)
#define EMBED_A 3 // amplitude/volume
#define EMBED_R 4 // pitch range/expression
#define EMBED_H 5 // echo/reverberation
-#define EMBED_T 6 // different tone for announcing punctuation
+#define EMBED_T 6 // different tone for announcing punctuation (not used)
#define EMBED_I 7 // sound icon
#define EMBED_S2 8 // speed (used in synthesize)
#define EMBED_Y 9 // say-as commands
@@ -61,13 +64,15 @@
#define EMBED_U 11 // audio uri
#define EMBED_B 12 // break
#define EMBED_F 13 // emphasis
+#define EMBED_C 14 // capital letter indication
-#define N_EMBEDDED_VALUES 14
+#define N_EMBEDDED_VALUES 15
extern int embedded_value[N_EMBEDDED_VALUES];
extern int embedded_default[N_EMBEDDED_VALUES];
#define N_PEAKS 9
+#define N_PEAKS2 9 // plus Notch and Fill (not yet implemented)
#define N_MARKERS 8
#define N_KLATTP 10 // this affects the phoneme data file format
@@ -87,7 +92,7 @@ extern int embedded_default[N_EMBEDDED_VALUES];
-typedef struct { // 44 bytes
+typedef struct { // 64 bytes
short frflags;
short ffreq[7];
unsigned char length;
@@ -100,8 +105,10 @@ typedef struct { // 44 bytes
unsigned char klattp2[5]; // continuation of klattp[], Avp, Fric, FricBP, Turb
unsigned char klatt_ap[7]; // Klatt parallel amplitude
unsigned char klatt_bp[7]; // Klatt parallel bandwidth /2
+ unsigned char spare; // pad to multiple of 4 bytes
} frame_t; // with extra Klatt parameters for parallel resonators
+
typedef struct { // 44 bytes
short frflags;
short ffreq[7];
@@ -112,32 +119,7 @@ typedef struct { // 44 bytes
unsigned char fright[3]; // width/4 f0-2
unsigned char bw[4]; // Klatt bandwidth BNZ /2, f1,f2,f3
unsigned char klattp[5]; // AV, FNZ, Tilt, Aspr, Skew
-} frame_t2; // TESTING
-
-
-#ifdef deleted
-typedef struct {
- short frflags;
- unsigned char length;
- unsigned char rms;
- short ffreq[9];
- unsigned char fheight[9];
- unsigned char fwidth[6]; // width/4
- unsigned char fright[6]; // width/4
- unsigned char fwidth6, fright6;
- unsigned char klattp[N_KLATTP];
-} frame_t;
-
-typedef struct { // 43 bytes
- short frflags;
- unsigned char length;
- unsigned char rms;
- short ffreq[9];
- unsigned char fheight[9];
- unsigned char fwidth[6]; // width/4
- unsigned char fright[6]; // width/4
-} frame_t2; // the original, without Klatt additions, used for file "phondata"
-#endif
+} frame_t2; // without the extra Klatt parameters
@@ -170,10 +152,12 @@ int n_mix_wavefile; // length in bytes
int mix_wave_scale; // 0=2 byte samples
int mix_wave_amp;
int mix_wavefile_ix;
+int mix_wavefile_max; // length of available WAV data (in bytes)
+int mix_wavefile_offset;
int amplitude;
int amplitude_v;
-int prev_was_synth; // previous sound was synthesized (not a played wave or pause)
+int amplitude_fmt; // percentage amplitude adjustment for formant synthesis
} WGEN_DATA;
@@ -189,14 +173,14 @@ typedef struct {
typedef struct {
short length_total; // not used
unsigned char n_frames;
- unsigned char flags;
+ unsigned char sqflags;
frame_t2 frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
} SPECT_SEQ; // sequence of espeak formant frames
typedef struct {
short length_total; // not used
unsigned char n_frames;
- unsigned char flags;
+ unsigned char sqflags;
frame_t frame[N_SEQ_FRAMES]; // max. frames in a spectrum sequence
} SPECT_SEQK; // sequence of klatt formants frames
@@ -207,24 +191,176 @@ typedef struct {
frame_t *frame;
} frameref_t;
+// a clause translated into phoneme codes (first stage)
+typedef struct {
+ unsigned short synthflags; // NOTE Put shorts on 32bit boundaries, because of RISC OS compiler bug?
+ unsigned char phcode;
+ unsigned char stresslevel;
+ unsigned short sourceix; // ix into the original source text string, only set at the start of a word
+ unsigned char wordstress; // the highest level stress in this word
+ unsigned char tone_ph; // tone phoneme to use with this vowel
+} PHONEME_LIST2;
+
typedef struct {
+// The first section is a copy of PHONEME_LIST2
+ unsigned short synthflags;
+ unsigned char phcode;
+ unsigned char stresslevel;
+ unsigned short sourceix; // ix into the original source text string, only set at the start of a word
+ unsigned char wordstress; // the highest level stress in this word
+ unsigned char tone_ph; // tone phoneme to use with this vowel
+
PHONEME_TAB *ph;
+ unsigned int length; // length_mod
unsigned char env; // pitch envelope number
- unsigned char stresslevel;
unsigned char type;
unsigned char prepause;
+ unsigned char postpause;
unsigned char amp;
- unsigned char tone_ph; // tone phoneme to use with this vowel
unsigned char newword; // bit 0=start of word, bit 1=end of clause, bit 2=start of sentence
- unsigned char synthflags;
- short length; // length_mod
- short pitch1; // pitch, 0-4095 within the Voice's pitch range
- short pitch2;
- unsigned short sourceix; // ix into the original source text string, only set at the start of a word
+ unsigned char pitch1;
+ unsigned char pitch2;
+#ifdef _ESPEAKEDIT
+ unsigned char std_length;
+ unsigned int phontab_addr;
+ int sound_param;
+#endif
} PHONEME_LIST;
+#define pd_FMT 0
+#define pd_WAV 1
+#define pd_VWLSTART 2
+#define pd_VWLEND 3
+#define pd_ADDWAV 4
+
+#define N_PHONEME_DATA_PARAM 16
+#define pd_INSERTPHONEME i_INSERT_PHONEME
+#define pd_APPENDPHONEME i_APPEND_PHONEME
+#define pd_CHANGEPHONEME i_CHANGE_PHONEME
+#define pd_CHANGE_NEXTPHONEME i_REPLACE_NEXT_PHONEME
+#define pd_LENGTHMOD i_SET_LENGTH
+
+#define pd_FORNEXTPH 0x2
+#define pd_DONTLENGTHEN 0x4
+#define pd_REDUCELENGTHCHANGE 0x8
+typedef struct {
+ int pd_control;
+ int pd_param[N_PHONEME_DATA_PARAM]; // set from group 0 instructions
+ int sound_addr[5];
+ int sound_param[5];
+ int vowel_transition[4];
+ int pitch_env;
+ int amp_env;
+ char ipa_string[18];
+} PHONEME_DATA;
+
+
+typedef struct {
+ int fmt_control;
+ int use_vowelin;
+ int fmt_addr;
+ int fmt_length;
+ int fmt_amp;
+ int fmt2_addr;
+ int fmt2_lenadj;
+ int wav_addr;
+ int wav_amp;
+ int transition0;
+ int transition1;
+ int std_length;
+} FMT_PARAMS;
+
+typedef struct {
+ PHONEME_LIST prev_vowel;
+} WORD_PH_DATA;
+
+// instructions
+
+#define i_RETURN 0x0001
+#define i_CONTINUE 0x0002
+#define i_NOT 0x0003
+
+// Group 0 instrcutions with 8 bit operand. These values go into bits 8-15 of the instruction
+#define i_CHANGE_PHONEME 0x01
+#define i_REPLACE_NEXT_PHONEME 0x02
+#define i_INSERT_PHONEME 0x03
+#define i_APPEND_PHONEME 0x04
+#define i_APPEND_IFNEXTVOWEL 0x05
+#define i_VOICING_SWITCH 0x06
+#define i_PAUSE_BEFORE 0x07
+#define i_PAUSE_AFTER 0x08
+#define i_LENGTH_MOD 0x09
+#define i_SET_LENGTH 0x0a
+#define i_LONG_LENGTH 0x0b
+#define i_CHANGE_PHONEME2 0x0c // not yet used
+#define i_IPA_NAME 0x0d
+
+#define i_CHANGE_IF 0x10 // 0x10 to 0x14
+
+#define i_ADD_LENGTH 0x0c
+
+
+// conditions and jumps
+#define i_CONDITION 0x2000
+#define i_OR 0x1000 // added to i_CONDITION
+
+#define i_JUMP 0x6000
+#define i_JUMP_FALSE 0x6800
+#define i_SWITCH_NEXTVOWEL 0x6a00
+#define i_SWITCH_PREVVOWEL 0x6c00
+#define MAX_JUMP 255 // max jump distance
+
+// multi-word instructions
+#define i_CALLPH 0x9100
+#define i_PITCHENV 0x9200
+#define i_AMPENV 0x9300
+#define i_VOWELIN 0xa100
+#define i_VOWELOUT 0xa200
+#define i_FMT 0xb000
+#define i_WAV 0xc000
+#define i_VWLSTART 0xd000
+#define i_VWLENDING 0xe000
+#define i_WAVADD 0xf000
+
+// conditions
+#define i_isDiminished 0x80
+#define i_isUnstressed 0x81
+#define i_isNotStressed 0x82
+#define i_isStressed 0x83
+#define i_isMaxStress 0x84
+
+#define i_isBreak 0x85
+#define i_isWordStart 0x86
+#define i_notWordStart 0x87
+#define i_isWordEnd 0x88
+#define i_isAfterStress 0x89
+#define i_isNotVowel 0x8a
+#define i_isFinalVowel 0x8b
+#define i_isVoiced 0x8c
+#define i_isFirstVowel 0x8d
+#define i_isSecondVowel 0x8e
+#define i_isSeqFlag1 0x8f
+#define i_IsTranslationGiven 0x90
+
+
+// place of articulation
+#define i_isVel 0x28
+
+// phflags
+#define i_isSibilant 0x45 // bit 5 in phflags
+#define i_isPalatal 0x49 // bit 9 in phflags
+#define i_isLong 0x55 // bit 21 in phflags
+#define i_isRhotic 0x57 // bit 23 in phflags
+#define i_isFlag1 0x5c
+#define i_isFlag2 0x5d
+#define i_isFlag3 0x5e
+
+#define i_StressLevel 0x800
+
+
+
typedef struct {
int name;
int length;
@@ -242,20 +378,71 @@ typedef struct {
} MBROLA_TAB;
typedef struct {
- int speed_factor1;
- int speed_factor2;
- int speed_factor3;
+ int pause_factor;
+ int clause_pause_factor;
+ unsigned int min_pause;
+ int wav_factor;
+ int lenmod_factor;
+ int lenmod2_factor;
int min_sample_len;
+ int loud_consonants;
int fast_settings[8];
} SPEED_FACTORS;
+typedef struct {
+ char name[12];
+ unsigned char flags[4];
+ signed char head_extend[8];
+
+ unsigned char prehead_start;
+ unsigned char prehead_end;
+ unsigned char stressed_env;
+ unsigned char stressed_drop;
+ unsigned char secondary_drop;
+ unsigned char unstressed_shape;
+
+ unsigned char onset;
+ unsigned char head_start;
+ unsigned char head_end;
+ unsigned char head_last;
+
+ unsigned char head_max_steps;
+ unsigned char n_head_extend;
+
+ signed char unstr_start[3]; // for: onset, head, last
+ signed char unstr_end[3];
+
+ unsigned char nucleus0_env; // pitch envelope, tonic syllable is at end, no tail
+ unsigned char nucleus0_max;
+ unsigned char nucleus0_min;
+
+ unsigned char nucleus1_env; // when followed by a tail
+ unsigned char nucleus1_max;
+ unsigned char nucleus1_min;
+ unsigned char tail_start;
+ unsigned char tail_end;
+
+ unsigned char split_nucleus_env;
+ unsigned char split_nucleus_max;
+ unsigned char split_nucleus_min;
+ unsigned char split_tail_start;
+ unsigned char split_tail_end;
+ unsigned char split_tune;
+
+ unsigned char spare[8];
+ int spare2; // the struct length should be a multiple of 4 bytes
+} TUNE;
+
+extern int n_tunes;
+extern TUNE *tunes;
+
// phoneme table
extern PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
// list of phonemes in a clause
extern int n_phoneme_list;
-extern PHONEME_LIST phoneme_list[N_PHONEME_LIST];
+extern PHONEME_LIST phoneme_list[N_PHONEME_LIST+1];
extern unsigned int embedded_list[];
extern unsigned char env_fall[128];
@@ -278,27 +465,31 @@ extern unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1];
#define WCMD_MARKER 10
#define WCMD_VOICE 11
#define WCMD_EMBEDDED 12
+#define WCMD_MBROLA_DATA 13
+#define WCMD_FMT_AMPLITUDE 14
+#define WCMD_SONIC_SPEED 15
+
-#define N_WCMDQ 160
-#define MIN_WCMDQ 22 // need this many free entries before adding new phoneme
+#define N_WCMDQ 170
+#define MIN_WCMDQ 25 // need this many free entries before adding new phoneme
-extern long wcmdq[N_WCMDQ][4];
+extern long64 wcmdq[N_WCMDQ][4];
extern int wcmdq_head;
extern int wcmdq_tail;
// from Wavegen file
-int WcmdqFree();
-void WcmdqStop();
-int WcmdqUsed();
-void WcmdqInc();
-int WavegenOpenSound();
-int WavegenCloseSound();
-int WavegenInitSound();
+int WcmdqFree(void);
+void WcmdqStop(void);
+int WcmdqUsed(void);
+void WcmdqInc(void);
+int WavegenOpenSound(void);
+int WavegenCloseSound(void);
+int WavegenInitSound(void);
void WavegenInit(int rate, int wavemult_fact);
float polint(float xa[],float ya[],int n,float x);
int WavegenFill(int fill_zeros);
-void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr);
+void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr);
extern unsigned char *wavefile_data;
@@ -312,15 +503,21 @@ extern int wavefile_amp2;
extern int vowel_transition[4];
extern int vowel_transition0, vowel_transition1;
+#define N_ECHO_BUF 5500 // max of 250mS at 22050 Hz
+extern int echo_head;
+extern int echo_tail;
+extern int echo_amp;
+extern short echo_buf[N_ECHO_BUF];
+
extern int mbrola_delay;
extern char mbrola_name[20];
// from synthdata file
unsigned int LookupSound(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int *match_level, int control);
-frameref_t *LookupSpect(PHONEME_TAB *ph1, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph, int which, int *match_level, int *n_frames, PHONEME_LIST *plist);
+frameref_t *LookupSpect(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, int *n_frames, PHONEME_LIST *plist);
unsigned char *LookupEnvelope(int ix);
-int LoadPhData();
+int LoadPhData(int *srate);
void SynthesizeInit(void);
int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume);
@@ -335,10 +532,16 @@ int SelectPhonemeTableName(const char *name);
void Write4Bytes(FILE *f, int value);
int Read4Bytes(FILE *f);
+int Reverse4Bytes(int word);
int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *err_name,int flags);
-extern unsigned char *envelope_data[18];
+#define ENV_LEN 128 // length of pitch envelopes
+#define PITCHfall 0 // standard pitch envelopes
+#define PITCHrise 2
+#define N_ENVELOPE_DATA 20
+extern unsigned char *envelope_data[N_ENVELOPE_DATA];
+
extern int formant_rate[]; // max rate of change of each formant
extern SPEED_FACTORS speed;
@@ -353,6 +556,7 @@ extern t_espeak_callback* synth_callback;
extern int option_log_frames;
extern const char *version_string;
extern const int version_phdata;
+extern double sonicSpeed;
#define N_SOUNDICON_TAB 80 // total entries in soundicon_tab
#define N_SOUNDICON_SLOTS 4 // number of slots reserved for dynamic loading of audio files
@@ -363,15 +567,24 @@ espeak_ERROR SetVoiceByName(const char *name);
espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector);
espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate);
void SetParameter(int parameter, int value, int relative);
-void MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, FILE *f_mbrola);
-//int MbrolaSynth(char *p_mbrola);
-int DoSample(PHONEME_TAB *ph1, PHONEME_TAB *ph2, int which, int length_mod, int amp);
-int DoSpect(PHONEME_TAB *this_ph, PHONEME_TAB *prev_ph, PHONEME_TAB *next_ph,
- int which, PHONEME_LIST *plist, int modulation);
+int MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola);
+int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume);
+int MbrolaFill(int length, int resume, int amplitude);
+void MbrolaReset(void);
+void DoEmbedded(int *embix, int sourceix);
+void DoMarker(int type, int char_posn, int length, int value);
+void DoPhonemeMarker(int type, int char_posn, int length, char *name);
+int DoSample3(PHONEME_DATA *phdata, int length_mod, int amp);
+int DoSpect2(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, PHONEME_LIST *plist, int modulation);
int PauseLength(int pause, int control);
int LookupPhonemeTable(const char *name);
+unsigned char *GetEnvelope(int index);
+int NumInstnWords(USHORT *prog);
void InitBreath(void);
-void KlattInit();
+void KlattInit(void);
+void KlattReset(int control);
int Wavegen_Klatt2(int length, int modulation, int resume, frame_t *fr1, frame_t *fr2);
+void DoSonicSpeed(int value);
+int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which);
diff --git a/navit/support/espeak/tr_languages.c b/navit/support/espeak/tr_languages.c
index 83d1c8041..b1913f205 100644
--- a/navit/support/espeak/tr_languages.c
+++ b/navit/support/espeak/tr_languages.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -39,22 +39,109 @@
#define L_qa 0x716100
#define L_grc 0x677263 // grc Ancient Greek
#define L_jbo 0x6a626f // jbo Lojban
-#define L_pap 0x706170 // pap Papiamento
+#define L_mni 0x6d6e69 // mni Manipuri
+#define L_pap 0x706170 // pap Papiamento]
+#define L_qvi 0x717669 // qvi Kichwa
+#define L_shs 0x736873 // shs Shuswap / Secwepemctsin
#define L_zhy 0x7a6879 // zhy
// start of unicode pages for character sets
-#define OFFSET_GREEK 0x380
+#define OFFSET_GREEK 0x380
#define OFFSET_CYRILLIC 0x420
#define OFFSET_ARMENIAN 0x530
+#define OFFSET_HEBREW 0x590
+#define OFFSET_ARABIC 0x600
+#define OFFSET_THAANA 0x780 // Divehi/Maldives
#define OFFSET_DEVANAGARI 0x900
-#define OFFSET_BENGALI 0x980
-#define OFFSET_TAMIL 0xb80
-#define OFFSET_KANNADA 0xc80
+#define OFFSET_BENGALI 0x980
+#define OFFSET_GURMUKHI 0xa00
+#define OFFSET_GUJARATI 0xa80
+#define OFFSET_ORIYA 0xb00
+#define OFFSET_TAMIL 0xb80
+#define OFFSET_TELUGU 0xc00
+#define OFFSET_KANNADA 0xc80
#define OFFSET_MALAYALAM 0xd00
-#define OFFSET_KOREAN 0x1100
+#define OFFSET_SINHALA 0x0d80
+#define OFFSET_THAI 0x0e00
+#define OFFSET_LAO 0x0e80
+#define OFFSET_TIBET 0x0f00
+#define OFFSET_MYANMAR 0x1000
+#define OFFSET_GEORGIAN 0x10a0
+#define OFFSET_KOREAN 0x1100
+#define OFFSET_ETHIOPIC 0x1200
+
+
+// character ranges must be listed in ascending order
+ALPHABET alphabets [] = {
+ {"_el", OFFSET_GREEK, 0x380, 0x3ff, L('e','l'), AL_DONT_NAME | AL_NOT_LETTERS},
+ {"_cyr", OFFSET_CYRILLIC, 0x400, 0x52f, 0, 0},
+ {"_hy", OFFSET_ARMENIAN, 0x530, 0x58f, L('h','y'), AL_WORDS},
+ {"_he", OFFSET_HEBREW, 0x590, 0x5ff, 0, 0},
+ {"_ar", OFFSET_ARABIC, 0x600, 0x6ff, 0, 0},
+ {"_dv", OFFSET_THAANA, 0x780, 0x7bf, 0, 0},
+ {"_hi", OFFSET_DEVANAGARI, 0x900, 0x97f,L('h','i'), AL_WORDS},
+ {"_bn", OFFSET_BENGALI, 0x0980, 0x9ff, L('b','n'), AL_WORDS},
+ {"_gur", OFFSET_GURMUKHI, 0xa00, 0xa7f, L('p','a'), AL_WORDS},
+ {"_gu", OFFSET_GUJARATI, 0xa80, 0xaff, L('g','u'), AL_WORDS},
+ {"_or", OFFSET_ORIYA, 0xb00, 0xb7f, 0, 0},
+ {"_ta", OFFSET_TAMIL, 0xb80, 0xbff, L('t','a'), AL_WORDS},
+ {"_te", OFFSET_TELUGU, 0xc00, 0xc7f, L('t','e'), 0},
+ {"_kn", OFFSET_KANNADA, 0xc80, 0xcff, L('k','n'), AL_WORDS},
+ {"_ml", OFFSET_MALAYALAM,0xd00, 0xd7f, L('m','l'), AL_WORDS},
+ {"_si", OFFSET_SINHALA, 0xd80, 0xdff, L('s','i'), AL_WORDS},
+ {"_th", OFFSET_THAI, 0xe00, 0xe7f, 0, 0},
+ {"_lo", OFFSET_LAO, 0xe80, 0xeff, 0, 0},
+ {"_ti", OFFSET_TIBET, 0xf00, 0xfff, 0, 0},
+ {"_my", OFFSET_MYANMAR, 0x1000,0x109f, 0, 0},
+ {"_ka", OFFSET_GEORGIAN, 0x10a0,0x10ff, L('k','a'), AL_WORDS},
+ {"_ko", OFFSET_KOREAN, 0x1100,0x11ff, L('k','o'), AL_WORDS},
+ {"_eth", OFFSET_ETHIOPIC, 0x1200,0x139f, 0, 0},
+ {"_braille", 0x2800, 0x2800,0x28ff, 0, AL_NO_SYMBOL},
+ {"_ja", 0x3040, 0x3040,0x30ff, 0, AL_NOT_CODE},
+ {"_zh", 0x3100, 0x3100,0x9fff, 0, AL_NOT_CODE},
+ {"_ko", 0xa700, 0xa700,0xd7ff, L('k','o'), AL_NOT_CODE | AL_WORDS},
+ {NULL, 0, 0, 0, 0, 0}
+};
+
+
+ALPHABET *AlphabetFromName(const char *name)
+{//==========================================
+ ALPHABET *alphabet;
+
+ for(alphabet=alphabets; alphabet->name != NULL; alphabet++)
+ {
+ if(strcmp(name, &alphabet->name[1]) == 0)
+ return(alphabet);
+ }
+ return(NULL);
+}
+
+
+ALPHABET *AlphabetFromChar(int c)
+{//===============================
+ // Find the alphabet from a character.
+ ALPHABET *alphabet = alphabets;
+
+ while(alphabet->name != NULL)
+ {
+ if(c <= alphabet->range_max)
+ {
+ if(c >= alphabet->range_min)
+ return(alphabet);
+ else
+ break;
+ }
+ alphabet++;
+ }
+ return(NULL);
+}
+
+
static void Translator_Russian(Translator *tr);
+
+
static void SetLetterVowel(Translator *tr, int c)
{//==============================================
tr->letter_bits[c] = (tr->letter_bits[c] & 0x40) | 0x81; // keep value for group 6 (front vowels e,i,y)
@@ -78,7 +165,7 @@ static void SetLetterBits(Translator *tr, int group, const char *string)
{//=====================================================================
int bits;
unsigned char c;
-
+
bits = (1L << group);
while((c = *string++) != 0)
tr->letter_bits[c] |= bits;
@@ -96,15 +183,58 @@ static void SetLetterBitsRange(Translator *tr, int group, int first, int last)
}
}
+// ignore these characters
+static const unsigned short chars_ignore_default[] = {
+ 0xad, 1, // soft hyphen
+ 0x200c, 1, // zero width non-joiner
+ 0x200d, 1, // zero width joiner
+ 0, 0 };
+
+// alternatively, ignore characters but allow zero-width-non-joiner (lang-fa)
+static const unsigned short chars_ignore_zwnj_hyphen[] = {
+ 0xad, 1, // soft hyphen
+ 0x640, 1, // igniore Arabic Tatweel (lang=FA)
+ 0x200c, '-', // zero width non-joiner, replace with hyphen
+ 0x200d, 1, // zero width joiner
+ 0, 0 };
+
+//const char string_ordinal[] = {0xc2,0xba,0}; // masculine ordinal character, UTF-8
+const char string_ordinal[] = "\xc2\xba"; // masculine ordinal character, UTF-8
+
static Translator* NewTranslator(void)
{//===================================
Translator *tr;
int ix;
- static const unsigned char stress_amps2[] = {17,17, 20,20, 20,22, 22,20 };
+ static const unsigned char stress_amps2[] = {18,18, 20,20, 20,22, 22,20 };
static const short stress_lengths2[8] = {182,140, 220,220, 220,240, 260,280};
static const wchar_t empty_wstring[1] = {0};
static const wchar_t punct_in_word[2] = {'\'', 0}; // allow hyphen within words
+ static const unsigned char default_tunes[6] = {0, 1, 2, 3, 0, 0};
+
+// Translates character codes in the range transpose_min to transpose_max to
+// a number in the range 1 to 63. 0 indicates there is no translation.
+// Used up to 57 (max of 63)
+static const char transpose_map_latin[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0x60
+16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 0, 0, 0, 0, 0, // 0x70
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xc0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xd0
+27, 28, 29, 0, 0, 30, 31, 32, 33, 34, 35, 36, 0, 37, 38, 0, // 0xe0
+ 0, 0, 0, 39, 0, 0, 40, 0, 41, 0, 42, 0, 43, 0, 0, 0, // 0xf0
+ 0, 0, 0, 44, 0, 45, 0, 46, 0, 0, 0, 0, 0, 47, 0, 0, // 0x100
+ 0, 48, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, // 0x110
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x120
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x130
+ 0, 0, 50, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x140
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, // 0x150
+ 0, 53, 0, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x160
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 56, 0, 57, 0, // 0x170
+};
tr = (Translator *)Alloc(sizeof(Translator));
if(tr == NULL)
@@ -112,18 +242,23 @@ static Translator* NewTranslator(void)
tr->charset_a0 = charsets[1]; // ISO-8859-1, this is for when the input is not utf8
dictionary_name[0] = 0;
+ tr->dictionary_name[0] = 0;
tr->dict_condition=0;
+ tr->dict_min_size = 0;
tr->data_dictrules = NULL; // language_1 translation rules file
tr->data_dictlist = NULL; // language_2 dictionary lookup file
- tr->transpose_offset = 0;
+ tr->transpose_min = 0x60;
+ tr->transpose_max = 0x17f;
+ tr->transpose_map = transpose_map_latin;
+ tr->frequent_pairs = NULL;
// only need lower case
tr->letter_bits_offset = 0;
memset(tr->letter_bits,0,sizeof(tr->letter_bits));
memset(tr->letter_groups,0,sizeof(tr->letter_groups));
- // 0-5 sets of characters matched by A B C E F G in pronunciation rules
+ // 0-6 sets of characters matched by A B C H F G Y in pronunciation rules
// these may be set differently for different languages
SetLetterBits(tr,0,"aeiou"); // A vowels, except y
SetLetterBits(tr,1,"bcdfgjklmnpqstvxz"); // B hard consonants, excluding h,r,w
@@ -137,6 +272,7 @@ static Translator* NewTranslator(void)
tr->char_plus_apostrophe = empty_wstring;
tr->punct_within_word = punct_in_word;
+ tr->chars_ignore = chars_ignore_default;
for(ix=0; ix<8; ix++)
{
@@ -145,16 +281,23 @@ static Translator* NewTranslator(void)
tr->stress_lengths[ix] = stress_lengths2[ix];
}
memset(&(tr->langopts),0,sizeof(tr->langopts));
+ tr->langopts.max_lengthmod = 500;
+ tr->langopts.lengthen_tonic = 20;
- tr->langopts.stress_rule = 2;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
tr->langopts.unstressed_wd1 = 1;
tr->langopts.unstressed_wd2 = 3;
tr->langopts.param[LOPT_SONORANT_MIN] = 95;
+ tr->langopts.param[LOPT_LONG_VOWEL_THRESHOLD] = 190/2;
tr->langopts.param[LOPT_MAXAMP_EOC] = 19;
tr->langopts.param[LOPT_UNPRONOUNCABLE] = 's'; // don't count this character at start of word
+ tr->langopts.param[LOPT_BRACKET_PAUSE] = 4; // pause at bracket
+ tr->langopts.param2[LOPT_BRACKET_PAUSE] = 2; // pauses when announcing bracket names
tr->langopts.max_initial_consonants = 3;
tr->langopts.replace_chars = NULL;
- tr->langopts.ascii_language = ""; // Non-Latin alphabet languages, use this language to speak Latin words, default is English
+ tr->langopts.ascii_language[0] = 0; // Non-Latin alphabet languages, use this language to speak Latin words, default is English
+ tr->langopts.alt_alphabet_lang = L('e','n');
+ tr->langopts.roman_suffix = "";
SetLengthMods(tr,201);
// tr->langopts.length_mods = length_mods_en;
@@ -163,16 +306,59 @@ static Translator* NewTranslator(void)
tr->langopts.long_stop = 100;
tr->langopts.max_roman = 49;
+ tr->langopts.min_roman = 2;
tr->langopts.thousands_sep = ',';
tr->langopts.decimal_sep = '.';
+ tr->langopts.break_numbers = BREAK_THOUSANDS; // 1000, 1000,000 1,000,000 etc
+ tr->langopts.max_digits = 14;
memcpy(tr->punct_to_tone, punctuation_to_tone, sizeof(tr->punct_to_tone));
+ memcpy(tr->langopts.tunes, default_tunes, sizeof(tr->langopts.tunes));
+
return(tr);
}
-
-static const unsigned int replace_cyrillic_latin[] =
+// common letter pairs, encode these as a single byte
+// 2 bytes, using the transposed character codes
+static const short pairs_ru[] = {
+0x010c, // ла 21052 0x23
+0x010e, // на 18400
+0x0113, // та 14254
+0x0301, // ав 31083
+0x030f, // ов 13420
+0x060e, // не 21798
+0x0611, // ре 19458
+0x0903, // ви 16226
+0x0b01, // ак 14456
+0x0b0f, // ок 17836
+0x0c01, // ал 13324
+0x0c09, // ил 16877
+0x0e01, // ан 15359
+0x0e06, // ен 13543 0x30
+0x0e09, // ин 17168
+0x0e0e, // нн 15973
+0x0e0f, // он 22373
+0x0e1c, // ын 15052
+0x0f03, // во 24947
+0x0f11, // ро 13552
+0x0f12, // со 16368
+0x100f, // оп 19054
+0x1011, // рп 17067
+0x1101, // ар 23967
+0x1106, // ер 18795
+0x1109, // ир 13797
+0x110f, // ор 21737
+0x1213, // тс 25076
+0x1220, // яс 14310
+0x7fff};
+//0x040f ог 12976
+//0x1306 ет 12826
+//0x0f0d мо 12688
+
+
+
+static const unsigned int replace_cyrillic_latin[] =
{0x430,'a',
0x431,'b',
0x446,'c',
@@ -209,25 +395,63 @@ static const unsigned int replace_cyrillic_latin[] =
0}; // ѓ ѕ ќ
-void SetIndicLetters(Translator *tr)
+static const unsigned char ru_vowels[] = {0x10,0x15,0x31,0x18,0x1e,0x23,0x2b,0x2d,0x2e,0x2f, 0xb9,0xc9,0x91,0x8f,0x36,0}; //also kazakh
+static const unsigned char ru_consonants[] = {0x11,0x12,0x13,0x14,0x16,0x17,0x19,0x1a,0x1b,0x1c,0x1d,0x1f,0x20,0x21,0x22,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2c, 0x73,0x7b,0x83,0x9b,0};
+
+static void SetCyrillicLetters(Translator *tr)
+{//===========================================
+ // character codes offset by 0x420
+ static const char ru_soft[] = {0x2c,0x19,0x27,0x29,0}; // letter group B [k ts; s;]
+ static const char ru_hard[] = {0x2a,0x16,0x26,0x28,0}; // letter group H [S Z ts]
+ static const char ru_nothard[] = {0x11,0x12,0x13,0x14,0x17,0x19,0x1a,0x1b,0x1c,0x1d,0x1f,0x20,0x21,0x22,0x24,0x25,0x27,0x29,0x2c,0};
+ static const char ru_voiced[] = {0x11,0x12,0x13,0x14,0x16,0x17,0}; // letter group G (voiced obstruents)
+ static const char ru_ivowels[] = {0x2c,0x2e,0x2f,0x31,0}; // letter group Y (iotated vowels & soft-sign)
+ tr->charset_a0 = charsets[18]; // KOI8-R
+ tr->transpose_min = 0x430; // convert cyrillic from unicode into range 0x01 to 0x22
+ tr->transpose_max = 0x451;
+ tr->transpose_map = NULL;
+ tr->frequent_pairs = pairs_ru;
+
+ tr->letter_bits_offset = OFFSET_CYRILLIC;
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBits(tr,LETTERGP_A,(char *)ru_vowels);
+ SetLetterBits(tr,LETTERGP_B,ru_soft);
+ SetLetterBits(tr,LETTERGP_C,(char *)ru_consonants);
+ SetLetterBits(tr,LETTERGP_H,ru_hard);
+ SetLetterBits(tr,LETTERGP_F,ru_nothard);
+ SetLetterBits(tr,LETTERGP_G,ru_voiced);
+ SetLetterBits(tr,LETTERGP_Y,ru_ivowels);
+ SetLetterBits(tr,LETTERGP_VOWEL2,(char *)ru_vowels);
+} // end of SetCyrillicLetters
+
+
+static void SetIndicLetters(Translator *tr)
{//=================================
// Set letter types for Indic scripts, Devanagari, Tamill, etc
- static const char dev_consonants2[] = {0x02,0x03,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f};
+ static const char dev_consonants2[] = {0x02,0x03,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x7b,0x7c,0x7e,0x7f,0};
+ static const char dev_vowels2[] = {0x60,0x61, 0x55,0x56,0x57,0x62,0x63,0}; // non-consecutive vowels and vowel-signs
memset(tr->letter_bits,0,sizeof(tr->letter_bits));
- SetLetterBitsRange(tr,LETTERGP_A,0x04,0x14); // vowel letters only
+ SetLetterBitsRange(tr,LETTERGP_A,0x04,0x14); // vowel letters
+ SetLetterBitsRange(tr,LETTERGP_A,0x3e,0x4d); // + vowel signs, and virama
+ SetLetterBits(tr,LETTERGP_A, dev_vowels2); // + extra vowels and vowel signs
+
SetLetterBitsRange(tr,LETTERGP_B,0x3e,0x4d); // vowel signs, and virama
+ SetLetterBits(tr,LETTERGP_B, dev_vowels2); // + extra vowels and vowel signs
SetLetterBitsRange(tr,LETTERGP_C,0x15,0x39); // the main consonant range
SetLetterBits(tr,LETTERGP_C,dev_consonants2); // + additional consonants
SetLetterBitsRange(tr,LETTERGP_Y,0x04,0x14); // vowel letters
SetLetterBitsRange(tr,LETTERGP_Y,0x3e,0x4c); // + vowel signs
+ SetLetterBits(tr,LETTERGP_Y, dev_vowels2); // + extra vowels and vowel signs
tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.suffix_add_e = tr->letter_bits_offset + 0x4d; //virama
}
-void SetupTranslator(Translator *tr, const short *lengths, const unsigned char *amps)
+
+static void SetupTranslator(Translator *tr, const short *lengths, const unsigned char *amps)
{//==================================================================================
if(lengths != NULL)
memcpy(tr->stress_lengths,lengths,sizeof(tr->stress_lengths));
@@ -241,15 +465,26 @@ Translator *SelectTranslator(const char *name)
int name2 = 0;
Translator *tr;
- static const unsigned char stress_amps_sk[8] = {17,17, 20,20, 20,22, 22,21 };
+ static const short stress_lengths_equal[8] = {230, 230, 230, 230, 0, 0, 230, 230};
+ static const unsigned char stress_amps_equal[8] = {19,19, 19,19, 19,19, 19,19 };
+
+ static const short stress_lengths_fr[8] = {190, 170, 190, 200, 0, 0, 190, 240};
+ static const unsigned char stress_amps_fr[8] = {18,16, 18,18, 18,18, 18,18 };
+
+ static const unsigned char stress_amps_sk[8] = {17,16, 20,20, 20,22, 22,21 };
static const short stress_lengths_sk[8] = {190,190, 210,210, 0,0, 210,210};
+ static const short stress_lengths_ta[8] = {200, 200, 210, 210, 0, 0, 230, 230};
+ static const short stress_lengths_ta2[8] = {230, 230, 240, 240, 0, 0, 260, 260};
+ static const unsigned char stress_amps_ta[8] = {18,18, 18,18, 20,20, 22,22 };
+
+ tr = NewTranslator();
+ strcpy(tr->dictionary_name, name);
+
// convert name string into a word of up to 4 characters, for the switch()
while(*name != 0)
name2 = (name2 << 8) + *name++;
- tr = NewTranslator();
-
switch(name2)
{
case L('a','f'):
@@ -257,18 +492,53 @@ Translator *SelectTranslator(const char *name)
static const short stress_lengths_af[8] = {170,140, 220,220, 0, 0, 250,270};
SetupTranslator(tr,stress_lengths_af,NULL);
- tr->langopts.stress_rule = 0;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
tr->langopts.vowel_pause = 0x30;
tr->langopts.param[LOPT_DIERESES] = 1;
tr->langopts.param[LOPT_PREFIXES] = 1;
SetLetterVowel(tr,'y'); // add 'y' to vowels
-
- tr->langopts.numbers = 0x8d1 + NUM_ROMAN;
+
+ tr->langopts.numbers = NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_SINGLE_AND | NUM_ROMAN | NUM_1900;
tr->langopts.accents = 1;
}
break;
+ case L('a','m'): // Amharic, Ethiopia
+ {
+ SetupTranslator(tr,stress_lengths_fr,stress_amps_fr);
+ tr->letter_bits_offset = OFFSET_ETHIOPIC;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_NO_AUTO_2 | S_FINAL_DIM; // don't use secondary stress
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.numbers = NUM_OMIT_1_HUNDRED;
+ }
+ break;
+
+
+ case L('a','r'): // Arabic
+ tr->letter_bits_offset = OFFSET_ARABIC;
+ tr->langopts.numbers = NUM_SWAP_TENS | NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_AND_HUNDRED | NUM_THOUSAND_AND | NUM_OMIT_1_THOUSAND;
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ break;
+
+ case L('b','g'): //Bulgarian
+ {
+ SetCyrillicLetters(tr);
+ SetLetterVowel(tr,0x2a);
+ tr->charset_a0 = charsets[5]; // ISO-8859-5
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 0x432; // [v] don't count this character at start of word
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x107; // devoice at end of word, and change voicing to match a following consonant (except v)
+ tr->langopts.param[LOPT_REDUCE] = 2;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_SINGLE_AND | NUM_ROMAN | NUM_ROMAN_ORDINAL | NUM_ROMAN_CAPITALS ;
+ tr->langopts.thousands_sep = ' '; // don't allow dot as thousands separator
+ }
+ break;
+
case L('b','n'): // Bengali
+ case L('a','s'): // Assamese
+ case L_mni: // Manipuri (temporary placement - it's not indo-european)
{
static const short stress_lengths_bn[8] = {180, 180, 210, 210, 0, 0, 230, 240};
static const unsigned char stress_amps_bn[8] = {18,18, 18,18, 20,20, 22,22 };
@@ -276,14 +546,28 @@ Translator *SelectTranslator(const char *name)
SetupTranslator(tr,stress_lengths_bn,stress_amps_bn);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
- tr->langopts.stress_rule = 0;
- tr->langopts.stress_flags = 0x10004; // use 'diminished' for unstressed final syllable
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_MID_DIM | S_FINAL_DIM; // use 'diminished' for unstressed final syllable
tr->letter_bits_offset = OFFSET_BENGALI;
SetIndicLetters(tr); // call this after setting OFFSET_BENGALI
+ SetLetterBitsRange(tr,LETTERGP_B,0x01,0x01); // candranindu
SetLetterBitsRange(tr,LETTERGP_F,0x3e,0x4c); // vowel signs, but not virama
- tr->langopts.numbers = 0x1;
- tr->langopts.numbers2 = NUM2_100000;
+ tr->langopts.numbers = NUM_SWAP_TENS;
+ tr->langopts.break_numbers = 0x24924aa8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi
+ }
+ break;
+
+ case L('b','o'): // Tibet
+ {
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->letter_bits_offset = OFFSET_TIBET;
+ SetLetterBitsRange(tr,LETTERGP_A,0x71,0x7d); // vowel signs
+ SetLetterBitsRange(tr,LETTERGP_B,0x71,0x81); // vowel signs and subjoined letters
+ SetLetterBitsRange(tr,LETTERGP_B,0x90,0xbc);
+ SetLetterBitsRange(tr,LETTERGP_C,0x40,0x6c); // consonant letters (not subjoined)
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.numbers = 1;
}
break;
@@ -296,16 +580,16 @@ Translator *SelectTranslator(const char *name)
tr->charset_a0 = charsets[14]; // ISO-8859-14
// tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
- tr->langopts.stress_rule = 2;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
// tr->langopts.intonation_group = 4;
// 'diminished' is an unstressed final syllable
- tr->langopts.stress_flags = 0x6 | 0x10;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2;
tr->langopts.unstressed_wd1 = 0;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_SONORANT_MIN] = 120; // limit the shortening of sonorants before short vowels
- tr->langopts.numbers = 0x401;
+ tr->langopts.numbers = NUM_OMIT_1_HUNDRED;
SetLetterVowel(tr,'w'); // add letter to vowels and remove from consonants
SetLetterVowel(tr,'y');
@@ -314,27 +598,47 @@ Translator *SelectTranslator(const char *name)
case L('d','a'): // Danish
{
- static const short stress_lengths_da[8] = {160,140, 200,200, 0,0, 220,210};
+ static const short stress_lengths_da[8] = {160,140, 200,200, 0,0, 220,230};
SetupTranslator(tr,stress_lengths_da,NULL);
- tr->langopts.stress_rule = 0;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.param[LOPT_PREFIXES] = 1;
SetLetterVowel(tr,'y');
- tr->langopts.numbers = 0x10c59;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_ORDINAL_DOT | NUM_1900 | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_ORDINAL;
}
break;
case L('d','e'):
{
- static const short stress_lengths_de[8] = {150,130, 200,200, 0, 0, 260,275};
- tr->langopts.stress_rule = 0;
+ static const short stress_lengths_de[8] = {150,130, 200,200, 0, 0, 270,270};
+ static const unsigned char stress_amps_de[] = {20,20, 20,20, 20,22, 22,20 };
+ SetupTranslator(tr, stress_lengths_de, stress_amps_de);
+ tr->langopts.stress_rule = STRESSPOSN_1L;
tr->langopts.word_gap = 0x8; // don't use linking phonemes
tr->langopts.vowel_pause = 0x30;
tr->langopts.param[LOPT_PREFIXES] = 1;
- memcpy(tr->stress_lengths,stress_lengths_de,sizeof(tr->stress_lengths));
-
- tr->langopts.numbers = 0x11419 + NUM_ROMAN;
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x100; // devoice at end of word
+ tr->langopts.param[LOPT_LONG_VOWEL_THRESHOLD] = 175/2;
+
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_ALLOW_SPACE | NUM_ORDINAL_DOT | NUM_ROMAN;
+// tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_ALLOW_SPACE | NUM_ORDINAL_DOT | NUM_ROMAN;
SetLetterVowel(tr,'y');
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use de_rules for unpronouncable rules
+ }
+ break;
+
+ case L('d','v'): // Divehi (Maldives)
+ {
+ SetupTranslator(tr,stress_lengths_ta,stress_amps_ta);
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+ tr->letter_bits_offset = OFFSET_THAANA;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_MID_DIM | S_FINAL_DIM; // use 'diminished' for unstressed final syllable
+ SetLetterBitsRange(tr,LETTERGP_B,0x26,0x30); // vowel signs, and virama
+ tr->langopts.break_numbers = 0x14a8; // 1000, 100,000 10,000,000
+ tr->langopts.numbers = 1;
}
break;
@@ -343,9 +647,13 @@ Translator *SelectTranslator(const char *name)
static const short stress_lengths_en[8] = {182,140, 220,220, 0,0, 248,275};
SetupTranslator(tr,stress_lengths_en,NULL);
- tr->langopts.stress_rule = 0;
- tr->langopts.numbers = 0x841 + NUM_ROMAN;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = 0x08;
+ tr->langopts.numbers = NUM_HUNDRED_AND | NUM_ROMAN | NUM_1900;
tr->langopts.param[LOPT_COMBINE_WORDS] = 2; // allow "mc" to cmbine with the following word
+ tr->langopts.suffix_add_e = 'e';
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use en_rules for unpronouncable rules
+ SetLetterBits(tr,6,"aeiouy"); // Group Y: vowels, including y
}
break;
@@ -357,10 +665,10 @@ Translator *SelectTranslator(const char *name)
// character codes offset by 0x380
static const char el_vowels[] = {0x10,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x35,0x37,0x39,0x3f,0x45,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0};
- static const char el_fvowels[] = {0x2d,0x2e,0x2f,0x35,0x37,0x39,0x45,0x4d,0}; // ε η ι υ έ ή ί ύ
- static const char el_voiceless[]= {0x38,0x3a,0x3e,0x40,0x42,0x43,0x44,0x46,0x47,0}; // θ κ ξ π ς σ τ φ χ
+ static const char el_fvowels[] = {0x2d,0x2e,0x2f,0x35,0x37,0x39,0x45,0x4d,0}; // ε η ι υ έ ή ί ύ _
+ static const char el_voiceless[]= {0x38,0x3a,0x3e,0x40,0x42,0x43,0x44,0x46,0x47,0}; // θ κ ξ π ς σ τ φ χ _
static const char el_consonants[]={0x32,0x33,0x34,0x36,0x38,0x3a,0x3b,0x3c,0x3d,0x3e,0x40,0x41,0x42,0x43,0x44,0x46,0x47,0x48,0};
- static const wchar_t el_char_apostrophe[] = {0x3c3,0}; // σ
+ static const wchar_t el_char_apostrophe[] = {0x3c3,0}; // σ _
SetupTranslator(tr,stress_lengths_el,stress_amps_el);
@@ -370,19 +678,20 @@ Translator *SelectTranslator(const char *name)
tr->letter_bits_offset = OFFSET_GREEK;
memset(tr->letter_bits,0,sizeof(tr->letter_bits));
SetLetterBits(tr,LETTERGP_A,el_vowels);
+ SetLetterBits(tr,LETTERGP_VOWEL2,el_vowels);
SetLetterBits(tr,LETTERGP_B,el_voiceless);
SetLetterBits(tr,LETTERGP_C,el_consonants);
- SetLetterBits(tr,LETTERGP_Y,el_fvowels); // front vowels: ε η ι υ
+ SetLetterBits(tr,LETTERGP_Y,el_fvowels); // front vowels: ε η ι υ _
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x6; // mark unstressed final syllables as diminished
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY; // mark unstressed final syllables as diminished
tr->langopts.unstressed_wd1 = 0;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_SONORANT_MIN] = 130; // limit the shortening of sonorants before short vowels
- tr->langopts.numbers = 0x109;
- tr->langopts.numbers2 = 0x2; // variant form of numbers before thousands
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA;
+ tr->langopts.numbers2 = 0x2 | NUM2_MULTIPLE_ORDINAL | NUM2_ORDINAL_NO_AND; // variant form of numbers before thousands
if(name2 == L_grc)
{
@@ -394,74 +703,120 @@ Translator *SelectTranslator(const char *name)
case L('e','o'):
{
- static const short stress_lengths_eo[8] = {145, 145, 230, 170, 0, 0, 360, 370};
+ static const short stress_lengths_eo[8] = {150, 150, 230, 180, 0, 0, 300, 320};
static const unsigned char stress_amps_eo[] = {16,14, 20,20, 20,22, 22,21 };
static const wchar_t eo_char_apostrophe[2] = {'l',0};
-
+
SetupTranslator(tr,stress_lengths_eo,stress_amps_eo);
tr->charset_a0 = charsets[3]; // ISO-8859-3
tr->char_plus_apostrophe = eo_char_apostrophe;
- tr->langopts.word_gap = 1;
+// tr->langopts.word_gap = 1;
tr->langopts.vowel_pause = 2;
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x6 | 0x10;
- tr->langopts.unstressed_wd1 = 3;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2;
+// tr->langopts.unstressed_wd1 = 3;
tr->langopts.unstressed_wd2 = 2;
- tr->langopts.numbers = 0x1409 + NUM_ROMAN;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_ALLOW_SPACE | NUM_ROMAN;
}
break;
case L('e','s'): // Spanish
+ case L('a','n'): // Aragonese
case L('c','a'): // Catalan
case L_pap: // Papiamento
{
- static const short stress_lengths_es[8] = {180, 210, 190, 190, 0, 0, 230, 260};
-// static const short stress_lengths_es[8] = {170, 200, 180, 180, 0, 0, 220, 250};
+ static const short stress_lengths_es[8] = {180, 190, 230, 180, 0, 0, 240, 270};
static const unsigned char stress_amps_es[8] = {16,12, 18,18, 20,20, 20,20 }; // 'diminished' is used to mark a quieter, final unstressed syllable
static const wchar_t ca_punct_within_word[] = {'\'',0xb7,0}; // ca: allow middle-dot within word
SetupTranslator(tr,stress_lengths_es,stress_amps_es);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
- tr->langopts.stress_rule = 2;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
// stress last syllable if it doesn't end in vowel or "s" or "n"
// 'diminished' is an unstressed final syllable
- tr->langopts.stress_flags = 0x200 | 0x6 | 0x10;
+ tr->langopts.stress_flags = S_FINAL_SPANISH | S_FINAL_DIM_ONLY | S_FINAL_NO_2;
tr->langopts.unstressed_wd1 = 0;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_SONORANT_MIN] = 120; // limit the shortening of sonorants before short vowels
- tr->langopts.numbers = 0x529 + NUM_ROMAN + NUM_ROMAN_AFTER;
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_ROMAN | NUM_ROMAN_AFTER;
+ tr->langopts.numbers2 = NUM2_MULTIPLE_ORDINAL | NUM2_ORDINAL_NO_AND;
if(name2 == L('c','a'))
{
+ // stress last syllable unless word ends with a vowel
tr->punct_within_word = ca_punct_within_word;
- tr->langopts.stress_flags = 0x200 | 0x6 | 0x30; // stress last syllable unless word ends with a vowel
+ tr->langopts.stress_flags = S_FINAL_SPANISH | S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_NO_AUTO_2;
+ }
+ else
+ if(name2 == L('a','n'))
+ {
+ tr->langopts.stress_flags = S_FINAL_SPANISH | S_FINAL_DIM_ONLY | S_FINAL_NO_2;
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_ROMAN | NUM_ROMAN_ORDINAL;
+ tr->langopts.numbers2 = NUM2_ORDINAL_NO_AND;
+ tr->langopts.roman_suffix = string_ordinal;
}
else
if(name2 == L_pap)
{
- tr->langopts.stress_flags = 0x100 | 0x6 | 0x30; // stress last syllable unless word ends with a vowel
+ // stress last syllable unless word ends with a vowel
+ tr->langopts.stress_flags = S_FINAL_STRESS_C | S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_NO_AUTO_2;
+ }
+ else
+ {
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use es_rules for unpronouncable rules
}
}
break;
-
case L('e','u'): // basque
{
static const short stress_lengths_eu[8] = {200, 200, 200, 200, 0, 0, 210, 230}; // very weak stress
static const unsigned char stress_amps_eu[8] = {16,16, 18,18, 18,18, 18,18 };
SetupTranslator(tr,stress_lengths_eu,stress_amps_eu);
- tr->langopts.stress_rule = 1; // ?? second syllable ??
- tr->langopts.numbers = 0x569 + NUM_VIGESIMAL;
+ tr->langopts.stress_rule = STRESSPOSN_2L; // ?? second syllable ??
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_VIGESIMAL;
+ }
+ break;
+
+
+ case L('f','a'): // Farsi
+ {
+ // Convert characters in the range 0x620 to 0x6cc to the range 1 to 63.
+ // 0 indicates no translation for this character
+ static const char transpose_map_fa[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0x620
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 0, 0, 0, 0, 0, // 0x630
+ 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, // 0x640
+ 42, 43, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x650
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x660
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, // 0x670
+ 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x680
+ 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, // 0x690
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 49, // 0x6a0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x6b0
+ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51 }; // 0x6c0
+ tr->transpose_min = 0x620;
+ tr->transpose_max = 0x6cc;
+ tr->transpose_map = transpose_map_fa;
+ tr->letter_bits_offset = OFFSET_ARABIC;
+
+ tr->langopts.numbers = NUM_AND_UNITS | NUM_HUNDRED_AND;
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+
+ tr->chars_ignore = chars_ignore_zwnj_hyphen; // replace ZWNJ by hyphen
}
break;
+ case L('e','t'): // Estonian
+ tr->charset_a0 = charsets[4]; // ISO-8859-4
+ // drop through to Finnish
case L('f','i'): // Finnish
{
static const unsigned char stress_amps_fi[8] = {18,16, 22,22, 20,22, 22,22 };
@@ -469,12 +824,12 @@ Translator *SelectTranslator(const char *name)
SetupTranslator(tr,stress_lengths_fi,stress_amps_fi);
- tr->langopts.stress_rule = 0;
- tr->langopts.stress_flags = 0x56; // move secondary stress from light to a following heavy syllable
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_2_TO_HEAVY; // move secondary stress from light to a following heavy syllable
tr->langopts.param[LOPT_IT_DOUBLING] = 1;
tr->langopts.long_stop = 130;
- tr->langopts.numbers = 0x1009;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA + NUM_ALLOW_SPACE;
SetLetterVowel(tr,'y');
// tr->langopts.max_initial_consonants = 2; // BUT foreign words may have 3
tr->langopts.spelling_stress = 1;
@@ -482,32 +837,37 @@ Translator *SelectTranslator(const char *name)
}
break;
-
case L('f','r'): // french
{
- static const short stress_lengths_fr[8] = {190, 170, 190, 200, 0, 0, 235, 240};
- static const unsigned char stress_amps_fr[8] = {18,16, 20,20, 20,22, 22,21 };
-
SetupTranslator(tr,stress_lengths_fr,stress_amps_fr);
- tr->langopts.stress_rule = 3; // stress on final syllable
- tr->langopts.stress_flags = 0x0024; // don't use secondary stress
+ tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable
+ tr->langopts.stress_flags = S_NO_AUTO_2 | S_FINAL_DIM; // don't use secondary stress
tr->langopts.param[LOPT_IT_LENGTHEN] = 1; // remove lengthen indicator from unstressed syllables
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+ tr->langopts.accents = 2; // Say "Capital" after the letter.
- tr->langopts.numbers = 0x1509 + 0x8000 + (NUM_NOPAUSE | NUM_ROMAN | NUM_VIGESIMAL);
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_NOPAUSE | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_AFTER | NUM_VIGESIMAL | NUM_DFRACTION_4;
SetLetterVowel(tr,'y');
}
break;
-#ifdef deleted
- case L('g','a'): // Irish Gaelic
+ case L('g','a'): // irish
+ case L('g','d'): // scots gaelic
{
- tr->langopts.stress_rule = 1;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_NO_AUTO_2; // don't use secondary stress
+ tr->langopts.numbers = NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND;
+ tr->langopts.accents = 2; // 'capital' after letter name
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 3; // don't count apostrophe
+ tr->langopts.param[LOPT_IT_LENGTHEN] = 1; // remove [:] phoneme from non-stressed syllables (Lang=gd)
}
break;
-#endif
case L('h','i'): // Hindi
case L('n','e'): // Nepali
+ case L('o','r'): // Oriya
+ case L('p','a'): // Punjabi
+ case L('g','u'): // Gujarati
{
static const short stress_lengths_hi[8] = {190, 190, 210, 210, 0, 0, 230, 250};
static const unsigned char stress_amps_hi[8] = {17,14, 20,19, 20,22, 22,21 };
@@ -517,10 +877,35 @@ Translator *SelectTranslator(const char *name)
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.stress_rule = 6; // stress on last heaviest syllable, excluding final syllable
- tr->langopts.stress_flags = 0x10004; // use 'diminished' for unstressed final syllable
- tr->langopts.numbers = 0x011;
- tr->langopts.numbers2 = NUM2_100000;
+ tr->langopts.stress_flags = S_MID_DIM | S_FINAL_DIM; // use 'diminished' for unstressed final syllable
+ tr->langopts.numbers = NUM_SWAP_TENS;
+ tr->langopts.break_numbers = 0x14aa8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi
tr->letter_bits_offset = OFFSET_DEVANAGARI;
+
+ if(name2 == L('p','a'))
+ {
+ tr->letter_bits_offset = OFFSET_GURMUKHI;
+ }
+ else
+ if(name2 == L('g','u'))
+ {
+SetupTranslator(tr,stress_lengths_equal,stress_amps_equal);
+ tr->letter_bits_offset = OFFSET_GUJARATI;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ }
+ else
+ if(name2 == L('n','e'))
+ {
+SetupTranslator(tr,stress_lengths_equal,stress_amps_equal);
+ tr->langopts.break_numbers = 0x2aaaa8;
+ tr->langopts.max_digits = 22;
+ tr->langopts.numbers2 |= NUM2_ENGLISH_NUMERALS;
+ }
+ else
+ if(name2 == L('o','r'))
+ {
+ tr->letter_bits_offset = OFFSET_ORIYA;
+ }
SetIndicLetters(tr);
}
break;
@@ -534,22 +919,25 @@ Translator *SelectTranslator(const char *name)
static const short stress_lengths_hr[8] = {180,160, 200,200, 0,0, 220,230};
static const short stress_lengths_sr[8] = {160,150, 200,200, 0,0, 250,260};
+ strcpy(tr->dictionary_name, "hbs");
+
if(name2 == L('s','r'))
SetupTranslator(tr,stress_lengths_sr,stress_amps_hr);
else
SetupTranslator(tr,stress_lengths_hr,stress_amps_hr);
tr->charset_a0 = charsets[2]; // ISO-8859-2
- tr->langopts.stress_rule = 0;
- tr->langopts.stress_flags = 0x10;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_FINAL_NO_2;
tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x3;
tr->langopts.max_initial_consonants = 5;
tr->langopts.spelling_stress = 1;
tr->langopts.accents = 1;
- tr->langopts.numbers = 0x140d + 0x4000 + NUM_ROMAN_UC;
- tr->langopts.numbers2 = 0x4a; // variant numbers before thousands,milliards
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_DECIMAL_COMMA | NUM_THOUS_SPACE | NUM_DFRACTION_2 | NUM_ROMAN_CAPITALS;
+ tr->langopts.numbers2 = 0xa + NUM2_THOUSANDS_VAR5; // variant numbers before thousands,milliards
tr->langopts.replace_chars = replace_cyrillic_latin;
+ tr->langopts.our_alphabet = OFFSET_CYRILLIC; // don't say "cyrillic" before letter names
SetLetterVowel(tr,'y');
SetLetterVowel(tr,'r');
@@ -557,6 +945,14 @@ Translator *SelectTranslator(const char *name)
break;
+ case L('h','t'): // Haitian Creole
+// memcpy(tr->stress_lengths,stress_lengths_fr,sizeof(tr->stress_lengths));
+ tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable
+ tr->langopts.stress_flags = S_NO_AUTO_2 | S_FINAL_DIM; // don't use secondary stress
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_OMIT_1_HUNDRED | NUM_NOPAUSE | NUM_ROMAN | NUM_VIGESIMAL | NUM_DFRACTION_4;
+ break;
+
+
case L('h','u'): // Hungarian
{
static const unsigned char stress_amps_hu[8] = {17,17, 19,19, 20,22, 22,21 };
@@ -566,14 +962,17 @@ Translator *SelectTranslator(const char *name)
tr->charset_a0 = charsets[2]; // ISO-8859-2
tr->langopts.vowel_pause = 0x20;
- tr->langopts.stress_rule = 0;
- tr->langopts.stress_flags = 0x8036;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_NO_AUTO_2 | 0x8000 | S_HYPEN_UNSTRESS;
tr->langopts.unstressed_wd1 = 2;
-// tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x4; // don't propagate over word boundaries
tr->langopts.param[LOPT_IT_DOUBLING] = 1;
- tr->langopts.param[LOPT_COMBINE_WORDS] = 99; // combine some prepositions with the following word
+ tr->langopts.param[LOPT_ANNOUNCE_PUNCT] = 2; // don't break clause before announcing . ? !
- tr->langopts.numbers = 0x1009 + 0xa000 + NUM_ROMAN + 0x10000;
+ tr->langopts.numbers = NUM_DFRACTION_5 | NUM_ALLOW_SPACE | NUM_ROMAN | NUM_ROMAN_ORDINAL | NUM_ROMAN_CAPITALS | NUM_ORDINAL_DOT | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND;
+ tr->langopts.thousands_sep = ' '; // don't allow dot as thousands separator
+ tr->langopts.decimal_sep = ',';
+ tr->langopts.max_roman = 899;
+ tr->langopts.min_roman = 1;
SetLetterVowel(tr,'y');
tr->langopts.spelling_stress = 1;
SetLengthMods(tr,3); // all equal
@@ -585,30 +984,35 @@ SetLengthMods(tr,3); // all equal
static const short stress_lengths_hy[8] = {250, 200, 250, 250, 0, 0, 250, 250};
static const char hy_vowels[] = {0x31, 0x35, 0x37, 0x38, 0x3b, 0x48, 0x55, 0};
static const char hy_consonants[] = {0x32,0x33,0x34,0x36,0x39,0x3a,0x3c,0x3d,0x3e,0x3f,
- 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x56,0};
+ 0x40,0x41,0x42,0x43,0x44, 0x46,0x47,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x56,0};
+ static const char hy_consonants2[] = {0x45,0};
SetupTranslator(tr,stress_lengths_hy,NULL);
- tr->langopts.stress_rule = 3; // default stress on final syllable
+ tr->langopts.stress_rule = STRESSPOSN_1R; // default stress on final syllable
tr->letter_bits_offset = OFFSET_ARMENIAN;
memset(tr->letter_bits,0,sizeof(tr->letter_bits));
SetLetterBits(tr,LETTERGP_A,hy_vowels);
+ SetLetterBits(tr,LETTERGP_VOWEL2,hy_vowels);
+ SetLetterBits(tr,LETTERGP_B,hy_consonants); // not including 'j'
SetLetterBits(tr,LETTERGP_C,hy_consonants);
+ SetLetterBits(tr,LETTERGP_C,hy_consonants2); // add 'j'
tr->langopts.max_initial_consonants = 6;
- tr->langopts.numbers = 0x409;
-// tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED;
+ // tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
}
break;
case L('i','d'): // Indonesian
+ case L('m','s'): // Malay
{
static const short stress_lengths_id[8] = {160, 200, 180, 180, 0, 0, 220, 240};
static const unsigned char stress_amps_id[8] = {16,18, 18,18, 20,22, 22,21 };
SetupTranslator(tr,stress_lengths_id,stress_amps_id);
- tr->langopts.stress_rule = 2;
- tr->langopts.numbers = 0x1009 + NUM_ROMAN;
- tr->langopts.stress_flags = 0x6 | 0x10;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_ROMAN;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2;
tr->langopts.accents = 2; // "capital" after letter name
}
break;
@@ -619,8 +1023,8 @@ SetLengthMods(tr,3); // all equal
static const wchar_t is_lettergroup_B[] = {'c','f','h','k','p','t','x',0xfe,0}; // voiceless conants, including 'þ' ?? 's'
SetupTranslator(tr,stress_lengths_is,NULL);
- tr->langopts.stress_rule = 0;
- tr->langopts.stress_flags = 0x10;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_FINAL_NO_2;
tr->langopts.param[LOPT_IT_LENGTHEN] = 0x11; // remove lengthen indicator from unstressed vowels
tr->langopts.param[LOPT_REDUCE] = 2;
@@ -629,7 +1033,7 @@ SetLengthMods(tr,3); // all equal
SetLetterBits(tr,3,"jvr"); // Letter group H
tr->letter_groups[1] = is_lettergroup_B;
SetLetterVowel(tr,'y');
- tr->langopts.numbers = 0x8e9;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SINGLE_AND | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_1900;
tr->langopts.numbers2 = 0x2;
}
break;
@@ -642,17 +1046,17 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_it,stress_amps_it);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x10 | 0x20000;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_NO_2 | S_PRIORITY_STRESS;
tr->langopts.vowel_pause = 1;
tr->langopts.unstressed_wd1 = 2;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_IT_LENGTHEN] = 2; // remove lengthen indicator from unstressed or non-penultimate syllables
- tr->langopts.param[LOPT_IT_DOUBLING] = 2; // double the first consonant if the previous word ends in a stressed vowel
+ tr->langopts.param[LOPT_IT_DOUBLING] = 1; // double the first consonant if the previous word ends in a stressed vowel (changed to =1, 23.01.2014 - only use if prev.word has $double)
tr->langopts.param[LOPT_SONORANT_MIN] = 130; // limit the shortening of sonorants before short vowels
tr->langopts.param[LOPT_REDUCE] = 1; // reduce vowels even if phonemes are specified in it_list
tr->langopts.param[LOPT_ALT] = 2; // call ApplySpecialAttributes2() if a word has $alt or $alt2
- tr->langopts.numbers = 0x2709 + NUM_ROMAN;
+ tr->langopts.numbers = NUM_SINGLE_VOWEL | NUM_OMIT_1_HUNDRED |NUM_DECIMAL_COMMA | NUM_ROMAN | NUM_DFRACTION_1 | NUM_ROMAN_CAPITALS | NUM_ROMAN_AFTER;
tr->langopts.accents = 2; // Say "Capital" after the letter.
SetLetterVowel(tr,'y');
}
@@ -664,12 +1068,71 @@ SetLengthMods(tr,3); // all equal
static const wchar_t jbo_punct_within_word[] = {'.',',','\'',0x2c8,0}; // allow period and comma within a word, also stress marker (from LOPT_CAPS_IN_WORD)
SetupTranslator(tr,stress_lengths_jbo,NULL);
- tr->langopts.stress_rule = 2;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
tr->langopts.vowel_pause = 0x20c; // pause before a word which starts with a vowel, or after a word which ends in a consonant
// tr->langopts.word_gap = 1;
tr->punct_within_word = jbo_punct_within_word;
tr->langopts.param[LOPT_CAPS_IN_WORD] = 2; // capitals indicate stressed syllables
SetLetterVowel(tr,'y');
+ tr->langopts.max_lengthmod = 368;
+ }
+ break;
+
+ case L('k','a'): // Georgian
+ {
+ // character codes offset by 0x1080
+ static const char ka_vowels[] = {0x30,0x34,0x38,0x3d,0x43,0x55,0x57,0};
+ static const char ka_consonants[] =
+ {0x31,0x32,0x33,0x35,0x36,0x37,0x39,0x3a,0x3b,0x3c,0x3e,0x3f,0x40,0x41,0x42,0x44,
+ 0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x56,0};
+ SetupTranslator(tr,stress_lengths_ta,stress_amps_ta);
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBits(tr,LETTERGP_A,ka_vowels);
+ SetLetterBits(tr,LETTERGP_C,ka_consonants);
+ SetLetterBits(tr,LETTERGP_VOWEL2,ka_vowels);
+
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_FINAL_NO_2;
+ tr->letter_bits_offset = OFFSET_GEORGIAN;
+// tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.max_initial_consonants = 7;
+ tr->langopts.numbers = NUM_VIGESIMAL | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED |NUM_OMIT_1_THOUSAND | NUM_DFRACTION_5 | NUM_ROMAN;
+
+ tr->langopts.alt_alphabet = OFFSET_CYRILLIC;
+ tr->langopts.alt_alphabet_lang = L('r','u');
+ }
+ break;
+
+ case L('k','k'): // Kazakh
+ {
+ static const unsigned char stress_amps_tr[8] = {18,16, 20,21, 20,21, 21,20 };
+ static const short stress_lengths_tr[8] = {190,180, 230,230, 0,0, 250,250};
+
+ tr->letter_bits_offset = OFFSET_CYRILLIC;
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBits(tr,LETTERGP_A,(char *)ru_vowels);
+ SetLetterBits(tr,LETTERGP_C,(char *)ru_consonants);
+ SetLetterBits(tr,LETTERGP_VOWEL2,(char *)ru_vowels);
+
+ SetupTranslator(tr,stress_lengths_tr,stress_amps_tr);
+
+ tr->langopts.stress_rule = 7; // stress on the last syllable, before any explicitly unstressed syllable
+ tr->langopts.stress_flags = S_NO_AUTO_2 + S_NO_EOC_LENGTHEN; //no automatic secondary stress, don't lengthen at end-of-clause
+ tr->langopts.lengthen_tonic = 0;
+ tr->langopts.param[LOPT_SUFFIX] = 1;
+
+ tr->langopts.numbers = NUM_OMIT_1_HUNDRED | NUM_DFRACTION_6 ;
+ tr->langopts.max_initial_consonants = 2;
+SetLengthMods(tr,3); // all equal
+ }
+ break;
+
+ case L('k','l'): // Greenlandic
+ {
+ SetupTranslator(tr,stress_lengths_equal,stress_amps_equal);
+ tr->langopts.stress_rule = 12;
+ tr->langopts.stress_flags = S_NO_AUTO_2;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_ORDINAL_DOT | NUM_1900 | NUM_ROMAN | NUM_ROMAN_CAPITALS | NUM_ROMAN_ORDINAL;
}
break;
@@ -679,6 +1142,7 @@ SetLengthMods(tr,3); // all equal
static const unsigned char ko_voiced[] = {0x02,0x05,0x06,0xab,0xaf,0xb7,0xbc,0}; // voiced consonants, l,m,n,N
tr->letter_bits_offset = OFFSET_KOREAN;
+ tr->langopts.our_alphabet = 0xa700;
memset(tr->letter_bits,0,sizeof(tr->letter_bits));
SetLetterBitsRange(tr,LETTERGP_A,0x61,0x75);
SetLetterBits(tr,LETTERGP_Y,ko_ivowels);
@@ -686,7 +1150,10 @@ SetLengthMods(tr,3); // all equal
tr->langopts.stress_rule = 8; // ?? 1st syllable if it is heavy, else 2nd syllable
tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
- tr->langopts.numbers = 0x0401;
+ tr->langopts.numbers = NUM_OMIT_1_HUNDRED;
+ tr->langopts.numbers2 = NUM2_MYRIADS;
+ tr->langopts.break_numbers = 0x1111110;
+ tr->langopts.max_digits = 20;
}
break;
@@ -700,7 +1167,7 @@ SetLengthMods(tr,3); // all equal
tr->langopts.stress_rule = 7; // stress on the last syllable, before any explicitly unstressed syllable
- tr->langopts.numbers = 0x100461;
+ tr->langopts.numbers = NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_AND_HUNDRED;
tr->langopts.max_initial_consonants = 2;
}
break;
@@ -708,12 +1175,26 @@ SetLengthMods(tr,3); // all equal
case L('l','a'): //Latin
{
tr->charset_a0 = charsets[4]; // ISO-8859-4, includes a,e,i,o,u-macron
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x20;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_NO_AUTO_2;
tr->langopts.unstressed_wd1 = 0;
tr->langopts.unstressed_wd2 = 2;
tr->langopts.param[LOPT_DIERESES] = 1;
- tr->langopts.numbers = 0x1 + NUM_ROMAN;
+ tr->langopts.numbers = NUM_ROMAN;
+ tr->langopts.max_roman = 5000;
+ }
+ break;
+
+ case L('l','t'): // Lithuanian
+ {
+ tr->charset_a0 = charsets[4]; // ISO-8859-4
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_NO_AUTO_2;
+ tr->langopts.unstressed_wd1 = 0;
+ tr->langopts.unstressed_wd2 = 2;
+ tr->langopts.param[LOPT_DIERESES] = 1;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_DFRACTION_4 | NUM_ORDINAL_DOT;
+ tr->langopts.numbers2 = NUM2_THOUSANDS_VAR4;
tr->langopts.max_roman = 5000;
}
break;
@@ -725,11 +1206,11 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_lv,stress_amps_lv);
- tr->langopts.stress_rule = 0;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
tr->langopts.spelling_stress = 1;
tr->charset_a0 = charsets[4]; // ISO-8859-4
- tr->langopts.numbers = 0x409 + 0x8000 + 0x10000;
- tr->langopts.stress_flags = 0x16 + 0x40000;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_DFRACTION_4 | NUM_ORDINAL_DOT;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_EO_CLAUSE1;
}
break;
@@ -742,38 +1223,50 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_mk,stress_amps_mk);
tr->charset_a0 = charsets[5]; // ISO-8859-5
- tr->letter_groups[0] = vowels_cyrillic;
+ tr->letter_groups[0] = tr->letter_groups[7] = vowels_cyrillic;
+ tr->letter_bits_offset = OFFSET_CYRILLIC;
- tr->langopts.stress_rule = 4; // antipenultimate
- tr->langopts.numbers = 0x0429 + 0x4000;
+ tr->langopts.stress_rule = STRESSPOSN_3R; // antipenultimate
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_AND_UNITS | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2;
tr->langopts.numbers2 = 0x8a; // variant numbers before thousands,milliards
}
break;
+ case L('m','t'): // Maltese
+ {
+ tr->charset_a0 = charsets[3]; // ISO-8859-3
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x100; // devoice at end of word
+ tr->langopts.stress_rule = STRESSPOSN_2R; // penultimate
+ tr->langopts.numbers = 1;
+ }
+ break;
case L('n','l'): // Dutch
{
static const short stress_lengths_nl[8] = {160,135, 210,210, 0, 0, 260,280};
- tr->langopts.stress_rule = 0;
- tr->langopts.vowel_pause = 1;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.vowel_pause = 0x30; // ??
tr->langopts.param[LOPT_DIERESES] = 1;
tr->langopts.param[LOPT_PREFIXES] = 1;
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x100; // devoice at end of word
SetLetterVowel(tr,'y');
-
- tr->langopts.numbers = 0x11c19;
+
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_SWAP_TENS | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_ALLOW_SPACE | NUM_1900 | NUM_ORDINAL_DOT;
+ tr->langopts.ordinal_indicator = "e";
+ tr->langopts.stress_flags = S_FIRST_PRIMARY;
memcpy(tr->stress_lengths,stress_lengths_nl,sizeof(tr->stress_lengths));
}
break;
case L('n','o'): // Norwegian
{
- static const short stress_lengths_no[8] = {160,140, 200,200, 0,0, 220,210};
+ static const short stress_lengths_no[8] = {160,140, 200,200, 0,0, 220,230};
SetupTranslator(tr,stress_lengths_no,NULL);
- tr->langopts.stress_rule = 0;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
SetLetterVowel(tr,'y');
- tr->langopts.numbers = 0x11849;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_ALLOW_SPACE | NUM_1900 | NUM_ORDINAL_DOT;
}
break;
@@ -783,8 +1276,8 @@ SetLengthMods(tr,3); // all equal
static const short stress_lengths_om[8] = {200,200, 200,200, 0,0, 200,200};
SetupTranslator(tr,stress_lengths_om,stress_amps_om);
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x16 + 0x80000;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2 | 0x80000;
}
break;
@@ -796,12 +1289,12 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_pl,stress_amps_pl);
tr->charset_a0 = charsets[2]; // ISO-8859-2
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x6; // mark unstressed final syllables as diminished
- tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x8;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY; // mark unstressed final syllables as diminished
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x9;
tr->langopts.max_initial_consonants = 7; // for example: wchrzczony :)
- tr->langopts.numbers=0x1009 + 0x4000;
- tr->langopts.numbers2=0x40;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_DFRACTION_2;
+ tr->langopts.numbers2 = NUM2_THOUSANDS_VAR3;
tr->langopts.param[LOPT_COMBINE_WORDS] = 4 + 0x100; // combine 'nie' (marked with $alt2) with some 1-syllable (and 2-syllable) words (marked with $alt)
SetLetterVowel(tr,'y');
}
@@ -809,18 +1302,22 @@ SetLengthMods(tr,3); // all equal
case L('p','t'): // Portuguese
{
- static const short stress_lengths_pt[8] = {180, 125, 210, 210, 0, 0, 270, 295};
- static const unsigned char stress_amps_pt[8] = {16,13, 19,19, 20,22, 22,21 }; // 'diminished' is used to mark a quieter, final unstressed syllable
+ static const short stress_lengths_pt[8] = {170, 115, 210, 240, 0, 0, 260, 280};
+ static const unsigned char stress_amps_pt[8] = {16,11, 19,21, 20,22, 22,21 }; // 'diminished' is used to mark a quieter, final unstressed syllable
SetupTranslator(tr,stress_lengths_pt,stress_amps_pt);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
- tr->langopts.stress_rule = 3; // stress on final syllable
- tr->langopts.stress_flags = 0x6 | 0x10 | 0x20000;
- tr->langopts.numbers = 0x069 + 0x4000 + NUM_ROMAN;
+ tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_INITIAL_2 | S_PRIORITY_STRESS;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_DFRACTION_2 | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_ROMAN_CAPITALS;
+ tr->langopts.numbers2 = NUM2_MULTIPLE_ORDINAL | NUM2_NO_TEEN_ORDINALS | NUM2_ORDINAL_NO_AND;
+ tr->langopts.max_roman = 5000;
SetLetterVowel(tr,'y');
ResetLetterBits(tr,0x2);
SetLetterBits(tr,1,"bcdfgjkmnpqstvxz"); // B hard consonants, excluding h,l,r,w,y
+ tr->langopts.param[LOPT_ALT] = 2; // call ApplySpecialAttributes2() if a word has $alt or $alt2
+ tr->langopts.accents = 2; // 'capital' after letter name
}
break;
@@ -831,11 +1328,11 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_ro,stress_amps_ro);
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x100 + 0x6;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_STRESS_C + S_FINAL_DIM_ONLY;
tr->charset_a0 = charsets[2]; // ISO-8859-2
- tr->langopts.numbers = 0x1029+0x6000 + NUM_ROMAN;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_DFRACTION_3 | NUM_AND_UNITS | NUM_ROMAN;
tr->langopts.numbers2 = 0x1e; // variant numbers before all thousandplex
}
break;
@@ -846,11 +1343,11 @@ SetLengthMods(tr,3); // all equal
case L('r','w'): // Kiryarwanda
{
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x16;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2;
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
-
- tr->langopts.numbers = 0x61 + 0x100000 + 0x4000;
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words. Need to allow "bw'" prefix
+ tr->langopts.numbers = NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_DFRACTION_2 | NUM_AND_HUNDRED;
tr->langopts.numbers2 = 0x200; // say "thousands" before its number
}
break;
@@ -863,16 +1360,16 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_sk,stress_amps_sk);
tr->charset_a0 = charsets[2]; // ISO-8859-2
- tr->langopts.stress_rule = 0;
- tr->langopts.stress_flags = 0x16;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2;
tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x3;
tr->langopts.max_initial_consonants = 5;
tr->langopts.spelling_stress = 1;
tr->langopts.param[LOPT_COMBINE_WORDS] = 4; // combine some prepositions with the following word
- tr->langopts.numbers = 0x0401 + 0x4000 + NUM_ROMAN;
- tr->langopts.numbers2 = 0x100;
- tr->langopts.thousands_sep = 0; //no thousands separator
+ tr->langopts.numbers = NUM_OMIT_1_HUNDRED | NUM_DFRACTION_2 | NUM_ROMAN;
+ tr->langopts.numbers2 = NUM2_THOUSANDS_VAR2;
+ tr->langopts.thousands_sep = STRESSPOSN_1L; //no thousands separator
tr->langopts.decimal_sep = ',';
if(name2 == L('c','s'))
@@ -887,6 +1384,47 @@ SetLengthMods(tr,3); // all equal
}
break;
+ case L('s','i'): // Sinhala
+ {
+ SetupTranslator(tr,stress_lengths_ta,stress_amps_ta);
+ tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
+
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2;
+ tr->langopts.spelling_stress = 1;
+
+ tr->letter_bits_offset = OFFSET_SINHALA;
+ memset(tr->letter_bits,0,sizeof(tr->letter_bits));
+ SetLetterBitsRange(tr,LETTERGP_A,0x05,0x16); // vowel letters
+ SetLetterBitsRange(tr,LETTERGP_A,0x4a,0x73); // + vowel signs, and virama
+
+ SetLetterBitsRange(tr,LETTERGP_B,0x4a,0x73); // vowel signs, and virama
+
+ SetLetterBitsRange(tr,LETTERGP_C,0x1a,0x46); // the main consonant range
+
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.suffix_add_e = tr->letter_bits_offset + 0x4a; //virama
+ tr->langopts.numbers = NUM_OMIT_1_THOUSAND | NUM_SINGLE_STRESS_L | NUM_DFRACTION_7;
+ tr->langopts.numbers2 = NUM2_PERCENT_BEFORE;
+ tr->langopts.break_numbers = 0x14aa8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi
+ }
+ break;
+
+ case L('s','l'): // Slovenian
+ tr->charset_a0 = charsets[2]; // ISO-8859-2
+ tr->langopts.stress_rule = STRESSPOSN_2R; // Temporary
+ tr->langopts.stress_flags = S_NO_AUTO_2;
+ tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 0x103;
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 0x76; // [v] don't count this character at start of word
+ tr->langopts.param[LOPT_ALT] = 2; // call ApplySpecialAttributes2() if a word has $alt or $alt2
+ tr->langopts.param[LOPT_IT_LENGTHEN] = 1; // remove lengthen indicator from unstressed syllables
+ tr->letter_bits['r'] |= 0x80; // add 'r' to letter group 7, vowels for Unpronouncable test
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_SWAP_TENS | NUM_OMIT_1_HUNDRED | NUM_DFRACTION_2 | NUM_ORDINAL_DOT | NUM_ROMAN;
+ tr->langopts.numbers2 = 0x100; // plural forms of millions etc
+ tr->langopts.thousands_sep = ' '; // don't allow dot as thousands separator
+ tr->langopts.replace_chars = replace_cyrillic_latin;
+ break;
+
case L('s','q'): // Albanian
{
static const short stress_lengths_sq[8] = {150, 150, 180, 180, 0, 0, 300, 300};
@@ -894,10 +1432,10 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_sq,stress_amps_sq);
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x16 + 0x100;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_FINAL_STRESS_C;
SetLetterVowel(tr,'y');
- tr->langopts.numbers = 0x69 + 0x8000;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND | NUM_AND_UNITS | NUM_DFRACTION_4;
tr->langopts.accents = 2; // "capital" after letter name
}
break;
@@ -909,14 +1447,15 @@ SetLengthMods(tr,3); // all equal
static const short stress_lengths_sv[8] = {160,135, 220,220, 0,0, 250,280};
SetupTranslator(tr,stress_lengths_sv,stress_amps_sv);
- tr->langopts.stress_rule = 0;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
SetLetterVowel(tr,'y');
- tr->langopts.numbers = 0x1909;
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_1900;
tr->langopts.accents = 1;
}
break;
case L('s','w'): // Swahili
+ case L('t','n'): // Setswana
{
static const short stress_lengths_sw[8] = {160, 170, 200, 200, 0, 0, 320, 340};
static const unsigned char stress_amps_sw[] = {16,12, 19,19, 20,22, 22,21 };
@@ -925,29 +1464,39 @@ SetLengthMods(tr,3); // all equal
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.vowel_pause = 1;
- tr->langopts.stress_rule = 2;
- tr->langopts.stress_flags = 0x6 | 0x10;
+ tr->langopts.stress_rule = STRESSPOSN_2R;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2;
+ tr->langopts.max_initial_consonants = 4; // for example: mwngi
+
- tr->langopts.numbers = 0x4e1;
- tr->langopts.numbers2 = NUM2_100000a;
+ tr->langopts.numbers = NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_SINGLE_AND | NUM_OMIT_1_HUNDRED;
+ tr->langopts.break_numbers = 0x49249268; // for languages which have numbers for 100,000 and 1,000,000
}
break;
case L('t','a'): // Tamil
- case L('m','l'): // Malayalam
case L('k','n'): // Kannada
+ case L('m','l'): // Malayalam
case L('m','r'): // Marathi
+ case L('t','e'): // Telugu
{
- static const short stress_lengths_ta[8] = {200, 200, 210, 210, 0, 0, 230, 230};
- static const unsigned char stress_amps_ta[8] = {18,18, 18,18, 20,20, 22,22 };
-
- SetupTranslator(tr,stress_lengths_ta,stress_amps_ta);
+ SetupTranslator(tr,stress_lengths_ta2, stress_amps_ta);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
- tr->langopts.stress_rule = 0;
- tr->langopts.stress_flags = 0x10004; // use 'diminished' for unstressed final syllable
- tr->letter_bits_offset = OFFSET_TAMIL;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.stress_flags = S_FINAL_DIM_ONLY | S_FINAL_NO_2; // use 'diminished' for unstressed final syllable
+ tr->langopts.spelling_stress = 1;
+ tr->langopts.break_numbers = 0x14a8; // 1000, 100,000 10,000,000
+ if(name2 == L('t','a'))
+ {
+ SetupTranslator(tr,stress_lengths_ta, NULL);
+ tr->letter_bits_offset = OFFSET_TAMIL;
+ tr->langopts.numbers = NUM_OMIT_1_THOUSAND ;
+ tr->langopts.numbers2 = NUM2_ORDINAL_AND_THOUSANDS;
+ tr->langopts.param[LOPT_WORD_MERGE] = 1; // don't break vowels betwen words
+ }
+ else
if(name2 == L('m','r'))
{
tr->letter_bits_offset = OFFSET_DEVANAGARI;
@@ -955,17 +1504,27 @@ SetLengthMods(tr,3); // all equal
else
if(name2 == L('m','l'))
{
+ static const short stress_lengths_ml[8] = {180, 160, 240, 240, 0, 0, 260, 260};
+ SetupTranslator(tr,stress_lengths_ml, stress_amps_equal);
tr->letter_bits_offset = OFFSET_MALAYALAM;
+ tr->langopts.numbers = NUM_OMIT_1_THOUSAND | NUM_OMIT_1_HUNDRED;
+ tr->langopts.numbers2 = NUM2_OMIT_1_HUNDRED_ONLY;
+ tr->langopts.stress_rule = 13; // 1st syllable, unless 1st vowel is short and 2nd is long
}
else
if(name2 == L('k','n'))
{
tr->letter_bits_offset = OFFSET_KANNADA;
tr->langopts.numbers = 0x1;
- tr->langopts.numbers2 = NUM2_100000;
}
- tr->langopts.param[LOPT_WORD_MERGE] = 1; // don't break vowels betwen words
+ else
+ if(name2 == L('t','e'))
+ {
+ tr->letter_bits_offset = OFFSET_TELUGU;
+ tr->langopts.numbers = 0x1;
+ }
SetIndicLetters(tr); // call this after setting OFFSET_
+ SetLetterBitsRange(tr,LETTERGP_B,0x4e,0x4e); // chillu-virama (unofficial)
}
break;
@@ -978,7 +1537,7 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_th,stress_amps_th);
tr->langopts.stress_rule = 0; // stress on final syllable of a "word"
- tr->langopts.stress_flags = 2; // don't automatically set diminished stress (may be set in the intonation module)
+ tr->langopts.stress_flags = S_NO_DIM; // don't automatically set diminished stress (may be set in the intonation module)
tr->langopts.tone_language = 1; // Tone language, use CalcPitches_Tone() rather than CalcPitches()
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
// tr->langopts.tone_numbers = 1; // a number after letters indicates a tone number (eg. pinyin or jyutping)
@@ -988,21 +1547,58 @@ SetLengthMods(tr,3); // all equal
#endif
case L('t','r'): // Turkish
+ case L('a','z'): // Azerbaijan
{
- static const unsigned char stress_amps_tr[8] = {18,18, 20,20, 20,22, 22,21 };
- static const short stress_lengths_tr[8] = {190,190, 190,190, 0,0, 250,270};
+ static const unsigned char stress_amps_tr[8] = {18,16, 20,21, 20,21, 21,20 };
+ static const short stress_lengths_tr[8] = {190,180, 200,230, 0,0, 240,250};
SetupTranslator(tr,stress_lengths_tr,stress_amps_tr);
tr->charset_a0 = charsets[9]; // ISO-8859-9 - Latin5
tr->langopts.stress_rule = 7; // stress on the last syllable, before any explicitly unstressed syllable
- tr->langopts.stress_flags = 0x20; //no automatic secondary stress
+ tr->langopts.stress_flags = S_NO_AUTO_2; //no automatic secondary stress
+ tr->langopts.dotless_i = 1;
+ tr->langopts.param[LOPT_SUFFIX] = 1;
- tr->langopts.numbers = 0x1509 + 0x4000;
+ if(name2 == L('a','z'))
+ {
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2;
+ }
+ else
+ {
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2;
+ }
tr->langopts.max_initial_consonants = 2;
}
break;
+ case L('t','t'): // Tatar
+ {
+ SetCyrillicLetters(tr);
+ SetupTranslator(tr,stress_lengths_fr,stress_amps_fr);
+ tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable
+ tr->langopts.stress_flags = S_NO_AUTO_2; //no automatic secondary stress
+ tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_4;
+ }
+ break;
+
+ case L('u','k'): // Ukrainian
+ {
+ SetCyrillicLetters(tr);
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 0x432; // [v] don't count this character at start of word
+ }
+ break;
+
+ case L('u','r'): // Urdu
+ case L('s','d'): // Sindhi
+ {
+ tr->letter_bits_offset = OFFSET_ARABIC;
+ tr->langopts.param[LOPT_UNPRONOUNCABLE] = 1; // disable check for unpronouncable words
+ tr->langopts.numbers = NUM_SWAP_TENS;
+ tr->langopts.break_numbers = 0x52a8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi
+ }
+ break;
+
case L('v','i'): // Vietnamese
{
static const short stress_lengths_vi[8] = {150, 150, 180, 180, 210, 230, 230, 240};
@@ -1019,22 +1615,27 @@ SetLengthMods(tr,3); // all equal
0x1a1, 0x1edd, 0x1edb, 0x1edf, 0x1ee1, 0x1ee3, // ơ
0x75, 0xf9, 0xfa, 0x1ee7, 0x169, 0x1ee5, // u
0x1b0, 0x1eeb, 0x1ee9, 0x1eed, 0x1eef, 0x1ef1, // ư
- 0x79, 0x1ef3, 0xfd, 0x1ef7, 0x1ef9, 0x1e, 0 }; // y
+ 0x79, 0x1ef3, 0xfd, 0x1ef7, 0x1ef9, 0x1ef5, 0 }; // y
SetupTranslator(tr,stress_lengths_vi,stress_amps_vi);
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
- tr->langopts.stress_rule = 0;
+ tr->langopts.stress_rule = STRESSPOSN_1L;
tr->langopts.word_gap = 0x21; // length of a final vowel is less dependent on the next consonant, don't merge consonant with next word
// tr->langopts.vowel_pause = 4;
- tr->letter_groups[0] = vowels_vi;
+ tr->letter_groups[0] = tr->letter_groups[7] = vowels_vi;
tr->langopts.tone_language = 1; // Tone language, use CalcPitches_Tone() rather than CalcPitches()
tr->langopts.unstressed_wd1 = 2;
- tr->langopts.numbers = 0x0049 + 0x8000;
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_HUNDRED_AND_DIGIT | NUM_DFRACTION_4 | NUM_ZERO_HUNDRED;
}
break;
+ case L('w','o'):
+ tr->langopts.stress_rule = STRESSPOSN_1L;
+ tr->langopts.numbers = NUM_AND_UNITS | NUM_HUNDRED_AND | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_SINGLE_STRESS;
+ break;
+
case L('z','h'):
case L_zhy:
{
@@ -1043,13 +1644,14 @@ SetLengthMods(tr,3); // all equal
SetupTranslator(tr,stress_lengths_zh,stress_amps_zh);
- tr->langopts.stress_rule = 3; // stress on final syllable of a "word"
- tr->langopts.stress_flags = 2; // don't automatically set diminished stress (may be set in the intonation module)
+ tr->langopts.stress_rule = STRESSPOSN_1R; // stress on final syllable of a "word"
+ tr->langopts.stress_flags = S_NO_DIM; // don't automatically set diminished stress (may be set in the intonation module)
tr->langopts.vowel_pause = 0;
tr->langopts.tone_language = 1; // Tone language, use CalcPitches_Tone() rather than CalcPitches()
tr->langopts.length_mods0 = tr->langopts.length_mods; // don't lengthen vowels in the last syllable
tr->langopts.tone_numbers = 1; // a number after letters indicates a tone number (eg. pinyin or jyutping)
tr->langopts.ideographs = 1;
+ tr->langopts.our_alphabet = 0x3100;
tr->langopts.word_gap = 0x21; // length of a final vowel is less dependent on the next consonant, don't merge consonant with next word
if(name2 == L('z','h'))
{
@@ -1066,66 +1668,48 @@ SetLengthMods(tr,3); // all equal
tr->translator_name = name2;
- if(tr->langopts.numbers & 0x8)
+ ProcessLanguageOptions(&tr->langopts);
+ return(tr);
+} // end of SelectTranslator
+
+
+void ProcessLanguageOptions(LANGUAGE_OPTIONS *langopts)
+{//=====================================================
+ if(langopts->numbers & NUM_DECIMAL_COMMA)
{
// use . and ; for thousands and decimal separators
- tr->langopts.thousands_sep = '.';
- tr->langopts.decimal_sep = ',';
+ langopts->thousands_sep = '.';
+ langopts->decimal_sep = ',';
}
- if(tr->langopts.numbers & 0x4)
+ if(langopts->numbers & NUM_THOUS_SPACE)
{
- tr->langopts.thousands_sep = 0; // don't allow thousands separator, except space
+ langopts->thousands_sep = 0; // don't allow thousands separator, except space
}
- return(tr);
-} // end of SelectTranslator
-
-
+}
//**********************************************************************************************************
+
static void Translator_Russian(Translator *tr)
{//===========================================
static const unsigned char stress_amps_ru[] = {16,16, 18,18, 20,24, 24,22 };
static const short stress_lengths_ru[8] = {150,140, 220,220, 0,0, 260,280};
-
-
- // character codes offset by 0x420
- static const char ru_vowels[] = {0x10,0x15,0x31,0x18,0x1e,0x23,0x2b,0x2d,0x2e,0x2f,0};
- static const char ru_consonants[] = {0x11,0x12,0x13,0x14,0x16,0x17,0x19,0x1a,0x1b,0x1c,0x1d,0x1f,0x20,0x21,0x22,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2c,0};
- static const char ru_soft[] = {0x2c,0x19,0x27,0x29,0}; // letter group B [k ts; s;]
- static const char ru_hard[] = {0x2a,0x16,0x26,0x28,0}; // letter group H [S Z ts]
- static const char ru_nothard[] = {0x11,0x12,0x13,0x14,0x17,0x19,0x1a,0x1b,0x1c,0x1d,0x1f,0x20,0x21,0x22,0x24,0x25,0x27,0x29,0x2c,0};
- static const char ru_voiced[] = {0x11,0x12,0x13,0x14,0x16,0x17,0}; // letter group G (voiced obstruents)
- static const char ru_ivowels[] = {0x2c,0x15,0x31,0x18,0x2e,0x2f,0}; // letter group Y (iotated vowels & soft-sign)
+ static const char ru_ivowels2[] = {0x2c,0x15,0x18,0x2e,0x2f,0}; // add more vowels to letter group Y (iotated vowels & soft-sign)
SetupTranslator(tr,stress_lengths_ru,stress_amps_ru);
-
- tr->charset_a0 = charsets[18]; // KOI8-R
- tr->transpose_offset = 0x42f; // convert cyrillic from unicode into range 0x01 to 0x22
- tr->transpose_min = 0x430;
- tr->transpose_max = 0x451;
-
- tr->letter_bits_offset = OFFSET_CYRILLIC;
- memset(tr->letter_bits,0,sizeof(tr->letter_bits));
- SetLetterBits(tr,0,ru_vowels);
- SetLetterBits(tr,1,ru_soft);
- SetLetterBits(tr,2,ru_consonants);
- SetLetterBits(tr,3,ru_hard);
- SetLetterBits(tr,4,ru_nothard);
- SetLetterBits(tr,5,ru_voiced);
- SetLetterBits(tr,6,ru_ivowels);
- SetLetterBits(tr,7,ru_vowels);
+ SetCyrillicLetters(tr);
+ SetLetterBits(tr,6,ru_ivowels2);
tr->langopts.param[LOPT_UNPRONOUNCABLE] = 0x432; // [v] don't count this character at start of word
tr->langopts.param[LOPT_REGRESSIVE_VOICING] = 1;
tr->langopts.param[LOPT_REDUCE] = 2;
tr->langopts.stress_rule = 5;
- tr->langopts.stress_flags = 0x0020; // waas 0x1010
+ tr->langopts.stress_flags = S_NO_AUTO_2;
- tr->langopts.numbers = 0x0409;
- tr->langopts.numbers2 = 0xc2; // variant numbers before thousands
+ tr->langopts.numbers = NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED;
+ tr->langopts.numbers2 = 0x2 + NUM2_THOUSANDS_VAR1; // variant numbers before thousands
tr->langopts.phoneme_change = 1;
tr->langopts.testing = 2;
@@ -1145,8 +1729,9 @@ typedef struct {
*/
-#define RUSSIAN2
+
#ifdef RUSSIAN2
+// This is now done in the phoneme data, ph_russian
int ChangePhonemes_ru(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
{//=============================================================================================================
@@ -1155,8 +1740,6 @@ int ChangePhonemes_ru(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index
int variant;
int vowelix;
- int stressed;
- int soft;
PHONEME_TAB *prev, *next;
if(ch->flags & 8)
@@ -1179,7 +1762,7 @@ PH('V','#'),PH('I','3'),PH('I','2'),PH('E','3')};
/*2*/ {'o', '8', '8', 'o', '8', '8'}, // O
/*3*/ {'i', 'I', 'i', 'a', 'I', 'a'}, // I Uses 3,4,5 columns.
/*4*/ {'i', PH('I','#'), 'i', 'i', PH('I','#'), 'i'}, // I#
- /*5*/ {'E', PH('E','#'), 'E', 'e', PH('E','#'), 'e'}, // E#
+ /*5*/ {'E', PH('E','#'), 'E', 'e', PH('E','#'), 'e'}, // E#
/*6*/ {'E', PH('E','2'), 'E', 'e', PH('E','2'), 'e'}, // E2 Uses 3,4,5 columns.
/*7*/ {PH('j','a'), 'V', PH('j','a'), 'A', 'V', 'A'}, // V#
/*8*/ {PH('j','a'), 'I', PH('j','a'), 'e', 'I', 'e'}, // I3 Uses 3,4,5 columns.
@@ -1216,8 +1799,8 @@ PH('V','#'),PH('I','3'),PH('I','2'),PH('E','3')};
}
// do we need a variant of this vowel, depending on the stress and adjacent phonemes ?
variant = -1;
- stressed = ch->flags & 2;
- soft=prev->phflags & phPALATAL;
+ int stressed = ch->flags & 2;
+ int soft=prev->phflags & phPALATAL;
if (soft && stressed)
variant = 2; else
@@ -1240,96 +1823,6 @@ PH('V','#'),PH('I','3'),PH('I','2'),PH('E','3')};
return(0);
}
-#else
-
-
-int ChangePhonemes_ru(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch)
-{//=============================================================================================================
-// Called for each phoneme in the phoneme list, to allow a language to make changes
-// flags: bit 0=1 last phoneme in a word
-// bit 1=1 this is the highest stressed vowel in the current word
-// bit 2=1 after the highest stressed vowel in the current word
-// bit 3=1 the phonemes were specified explicitly, or found from an entry in the xx_list dictionary
-// ph The current phoneme
-
- int variant;
- int vowelix;
- PHONEME_TAB *prev, *next;
-
- if(ch->flags & 8)
- return(0); // full phoneme translation has already been given
-
- // Russian vowel softening and reduction rules
- if(ph->type == phVOWEL)
- {
- #define N_VOWELS_RU 7
- static unsigned char vowels_ru[N_VOWELS_RU] = {'a','A','o','E','i','u','y'};
-
- // each line gives: soft, reduced, soft-reduced, post-tonic
- static unsigned short vowel_replace[N_VOWELS_RU][4] = {
- {'&', 'V', 'I', 'V'}, // a
- {'&', 'V', 'I', 'V'}, // A
- {'8', 'V', 'I', 'V'}, // o
- {'e', 'I', 'I', 'I'}, // E
- {'i', 'I', 'I', 'I'}, // i
- {'u'+('"'<<8), 'U', 'U', 'U'}, // u
- {'y', 'Y', 'Y', 'Y'}}; // y
-
- prev = phoneme_tab[phlist[index-1].phcode];
- next = phoneme_tab[phlist[index+1].phcode];
-
-if(prev->mnemonic == 'j')
- return(0);
-
- // lookup the vowel name to get an index into the vowel_replace[] table
- for(vowelix=0; vowelix<N_VOWELS_RU; vowelix++)
- {
- if(vowels_ru[vowelix] == ph->mnemonic)
- break;
- }
- if(vowelix == N_VOWELS_RU)
- return(0);
-
- // do we need a variant of this vowel, depending on the stress and adjacent phonemes ?
- variant = -1;
- if(ch->flags & 2)
- {
- // a stressed vowel
- if((prev->phflags & phPALATAL) && ((next->phflags & phPALATAL) || phoneme_tab[phlist[index+2].phcode]->mnemonic == ';'))
- {
- // between two palatal consonants, use the soft variant
- variant = 0;
- }
- }
- else
- {
- // an unstressed vowel
- if(prev->phflags & phPALATAL)
- {
- variant = 2; // unstressed soft
- }
- else
- if((ph->mnemonic == 'o') && ((prev->phflags & phPLACE) == phPLACE_pla))
- {
- variant = 2; // unstressed soft ([o] vowel following: ш ж
- }
- else
- if(ch->flags & 4)
- {
- variant = 3; // post tonic
- }
- else
- {
- variant = 1; // unstressed
- }
- }
- if(variant >= 0)
- {
- phlist[index].phcode = PhonemeCode(vowel_replace[vowelix][variant]);
- }
- }
- return(0);
-}
#endif
diff --git a/navit/support/espeak/translate.c b/navit/support/espeak/translate.c
index 4891644cd..890fa31ba 100755..100644
--- a/navit/support/espeak/translate.c
+++ b/navit/support/espeak/translate.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -75,17 +75,23 @@ static int embedded_flag = 0; // there are embedded commands to be applied to
static int prev_clause_pause=0;
static int max_clause_pause = 0;
+static int any_stressed_words;
int pre_pause;
+ALPHABET *current_alphabet;
// these were previously in translator class
+#ifdef PLATFORM_WINDOWS
+char word_phonemes[N_WORD_PHONEMES*2]; // longer, because snprint() is not available
+#else
char word_phonemes[N_WORD_PHONEMES]; // a word translated into phoneme codes
+#endif
int n_ph_list2;
PHONEME_LIST2 ph_list2[N_PHONEME_LIST]; // first stage of text->phonemes
-wchar_t option_punctlist[N_PUNCTLIST]={0};
+wchar_t option_punctlist[N_PUNCTLIST]= {0};
char ctrl_embedded = '\001'; // to allow an alternative CTRL for embedded commands
int option_multibyte=espeakCHARS_AUTO; // 0=auto, 1=utf8, 2=8bit, 3=wchar, 4=16bit
@@ -98,7 +104,6 @@ static int embedded_read;
unsigned int embedded_list[N_EMBEDDED_LIST];
// the source text of a single clause (UTF8 bytes)
-#define N_TR_SOURCE 700
static char source[N_TR_SOURCE+40]; // extra space for embedded command & voice change info at end
int n_replace_phonemes;
@@ -107,168 +112,170 @@ REPLACE_PHONEMES replace_phonemes[N_REPLACE_PHONEMES];
// brackets, also 0x2014 to 0x021f which don't need to be in this list
static const unsigned short brackets[] = {
-'(',')','[',']','{','}','<','>','"','\'','`',
-0xab,0xbb, // double angle brackets
-0x300a,0x300b, // double angle brackets (ideograph)
-0};
+ '(',')','[',']','{','}','<','>','"','\'','`',
+ 0xab,0xbb, // double angle brackets
+ 0x300a,0x300b, // double angle brackets (ideograph)
+ 0xe000+'<', // private usage area
+ 0
+};
// other characters which break a word, but don't produce a pause
static const unsigned short breaks[] = {'_', 0};
// treat these characters as spaces, in addition to iswspace()
-static const wchar_t chars_space[] = {0x2500,0}; // box drawing horiz
+// static const wchar_t chars_space[] = {0x2500,0x2501,0}; // box drawing horiz
// Translate character codes 0xA0 to 0xFF into their unicode values
// ISO_8859_1 is set as default
static const unsigned short ISO_8859_1[0x60] = {
- 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
- 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, // a8
- 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
- 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, // b8
- 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
- 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
- 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
- 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, // d8
- 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
- 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
- 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
- 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff, // f8
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, // a8
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, // b8
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, // d8
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff, // f8
};
static const unsigned short ISO_8859_2[0x60] = {
- 0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, // a0
- 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, // a8
- 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, // b0
- 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, // b8
- 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, // c0
- 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, // c8
- 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, // d0
- 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, // d8
- 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, // e0
- 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, // e8
- 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, // f0
- 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, // f8
+ 0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, // a0
+ 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, // a8
+ 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, // b0
+ 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, // b8
+ 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, // c0
+ 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, // c8
+ 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, // d0
+ 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, // d8
+ 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, // e0
+ 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, // e8
+ 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, // f0
+ 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, // f8
};
static const unsigned short ISO_8859_3[0x60] = {
- 0x00a0, 0x0126, 0x02d8, 0x00a3, 0x00a4, 0x0000, 0x0124, 0x00a7, // a0
- 0x00a8, 0x0130, 0x015e, 0x011e, 0x0134, 0x00ad, 0x0000, 0x017b, // a8
- 0x00b0, 0x0127, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x0125, 0x00b7, // b0
- 0x00b8, 0x0131, 0x015f, 0x011f, 0x0135, 0x00bd, 0x0000, 0x017c, // b8
- 0x00c0, 0x00c1, 0x00c2, 0x0000, 0x00c4, 0x010a, 0x0108, 0x00c7, // c0
- 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
- 0x0000, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x0120, 0x00d6, 0x00d7, // d0
- 0x011c, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x016c, 0x015c, 0x00df, // d8
- 0x00e0, 0x00e1, 0x00e2, 0x0000, 0x00e4, 0x010b, 0x0109, 0x00e7, // e0
- 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
- 0x0000, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x0121, 0x00f6, 0x00f7, // f0
- 0x011d, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x016d, 0x015d, 0x02d9, // f8
+ 0x00a0, 0x0126, 0x02d8, 0x00a3, 0x00a4, 0x0000, 0x0124, 0x00a7, // a0
+ 0x00a8, 0x0130, 0x015e, 0x011e, 0x0134, 0x00ad, 0x0000, 0x017b, // a8
+ 0x00b0, 0x0127, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x0125, 0x00b7, // b0
+ 0x00b8, 0x0131, 0x015f, 0x011f, 0x0135, 0x00bd, 0x0000, 0x017c, // b8
+ 0x00c0, 0x00c1, 0x00c2, 0x0000, 0x00c4, 0x010a, 0x0108, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
+ 0x0000, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x0120, 0x00d6, 0x00d7, // d0
+ 0x011c, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x016c, 0x015c, 0x00df, // d8
+ 0x00e0, 0x00e1, 0x00e2, 0x0000, 0x00e4, 0x010b, 0x0109, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
+ 0x0000, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x0121, 0x00f6, 0x00f7, // f0
+ 0x011d, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x016d, 0x015d, 0x02d9, // f8
};
static const unsigned short ISO_8859_4[0x60] = {
- 0x00a0, 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7, // a0
- 0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af, // a8
- 0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7, // b0
- 0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b, // b8
- 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, // c0
- 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a, // c8
- 0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
- 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df, // d8
- 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, // e0
- 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b, // e8
- 0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
- 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9, // f8
+ 0x00a0, 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7, // a0
+ 0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af, // a8
+ 0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7, // b0
+ 0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b, // b8
+ 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, // c0
+ 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a, // c8
+ 0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+ 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df, // d8
+ 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, // e0
+ 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b, // e8
+ 0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+ 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9, // f8
};
static const unsigned short ISO_8859_5[0x60] = {
- 0x00a0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, // a0 Cyrillic
- 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f, // a8
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, // b0
- 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, // b8
- 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, // c0
- 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, // c8
- 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, // d0
- 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, // d8
- 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, // e0
- 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, // e8
- 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, // f0
- 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f, // f8
+ 0x00a0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, // a0 Cyrillic
+ 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f, // a8
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, // b0
+ 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, // b8
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, // c0
+ 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, // c8
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, // d0
+ 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, // d8
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, // e0
+ 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, // e8
+ 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, // f0
+ 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f, // f8
};
static const unsigned short ISO_8859_7[0x60] = {
- 0x00a0, 0x2018, 0x2019, 0x00a3, 0x20ac, 0x20af, 0x00a6, 0x00a7, // a0 Greek
- 0x00a8, 0x00a9, 0x037a, 0x00ab, 0x00ac, 0x00ad, 0x0000, 0x2015, // a8
- 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x0385, 0x0386, 0x00b7, // b0
- 0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f, // b8
- 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, // c0
- 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, // c8
- 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, // d0
- 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, // d8
- 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, // e0
- 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, // e8
- 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, // f0
- 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x0000, // f8
+ 0x00a0, 0x2018, 0x2019, 0x00a3, 0x20ac, 0x20af, 0x00a6, 0x00a7, // a0 Greek
+ 0x00a8, 0x00a9, 0x037a, 0x00ab, 0x00ac, 0x00ad, 0x0000, 0x2015, // a8
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x0385, 0x0386, 0x00b7, // b0
+ 0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f, // b8
+ 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, // c0
+ 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, // c8
+ 0x03a0, 0x03a1, 0x0000, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, // d0
+ 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, // d8
+ 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, // e0
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, // e8
+ 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, // f0
+ 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x0000, // f8
};
static const unsigned short ISO_8859_9[0x60] = {
- 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
- 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, // a8
- 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
- 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, // b8
- 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
- 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
- 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
- 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, // d8
- 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
- 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
- 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
- 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff, // f8
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, // a8
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, // b8
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
+ 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, // d8
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
+ 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff, // f8
};
static const unsigned short ISO_8859_14[0x60] = {
- 0x00a0, 0x1e02, 0x1e03, 0x00a3, 0x010a, 0x010b, 0x1e0a, 0x00a7, // a0 Welsh
- 0x1e80, 0x00a9, 0x1e82, 0x1e0b, 0x1ef2, 0x00ad, 0x00ae, 0x0178, // a8
- 0x1e1e, 0x1e1f, 0x0120, 0x0121, 0x1e40, 0x1e41, 0x00b6, 0x1e56, // b0
- 0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61, // b8
- 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
- 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
- 0x0174, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x1e6a, // d0
- 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x0176, 0x00df, // d8
- 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
- 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
- 0x0175, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x1e6b, // f0
- 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x0177, 0x00ff, // f8
+ 0x00a0, 0x1e02, 0x1e03, 0x00a3, 0x010a, 0x010b, 0x1e0a, 0x00a7, // a0 Welsh
+ 0x1e80, 0x00a9, 0x1e82, 0x1e0b, 0x1ef2, 0x00ad, 0x00ae, 0x0178, // a8
+ 0x1e1e, 0x1e1f, 0x0120, 0x0121, 0x1e40, 0x1e41, 0x00b6, 0x1e56, // b0
+ 0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61, // b8
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, // c8
+ 0x0174, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x1e6a, // d0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x0176, 0x00df, // d8
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, // e8
+ 0x0175, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x1e6b, // f0
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x0177, 0x00ff, // f8
};
static const unsigned short KOI8_R[0x60] = {
- 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, // a0 Russian
- 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, 0x255e, // a8
- 0x255f, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, // b0
- 0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x00a9, // b8
- 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, // c0
- 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, // c8
- 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, // d0
- 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, // d8
- 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, // e0
- 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, // e8
- 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, // f0
- 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a, // f8
+ 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, // a0 Russian
+ 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, 0x255e, // a8
+ 0x255f, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, // b0
+ 0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x00a9, // b8
+ 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, // c0
+ 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, // c8
+ 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, // d0
+ 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, // d8
+ 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, // e0
+ 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, // e8
+ 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, // f0
+ 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a, // f8
};
static const unsigned short ISCII[0x60] = {
- 0x0020, 0x0901, 0x0902, 0x0903, 0x0905, 0x0906, 0x0907, 0x0908, // a0
- 0x0909, 0x090a, 0x090b, 0x090e, 0x090f, 0x0910, 0x090d, 0x0912, // a8
- 0x0913, 0x0914, 0x0911, 0x0915, 0x0916, 0x0917, 0x0918, 0x0919, // b0
- 0x091a, 0x091b, 0x091c, 0x091d, 0x091e, 0x091f, 0x0920, 0x0921, // b8
- 0x0922, 0x0923, 0x0924, 0x0925, 0x0926, 0x0927, 0x0928, 0x0929, // c0
- 0x092a, 0x092b, 0x092c, 0x092d, 0x092e, 0x092f, 0x095f, 0x0930, // c8
- 0x0931, 0x0932, 0x0933, 0x0934, 0x0935, 0x0936, 0x0937, 0x0938, // d0
- 0x0939, 0x0020, 0x093e, 0x093f, 0x0940, 0x0941, 0x0942, 0x0943, // d8
- 0x0946, 0x0947, 0x0948, 0x0945, 0x094a, 0x094b, 0x094c, 0x0949, // e0
- 0x094d, 0x093c, 0x0964, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // e8
- 0x0020, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, // f0
- 0x0037, 0x0038, 0x0039, 0x20, 0x20, 0x20, 0x20, 0x20, // f8
+ 0x0020, 0x0901, 0x0902, 0x0903, 0x0905, 0x0906, 0x0907, 0x0908, // a0
+ 0x0909, 0x090a, 0x090b, 0x090e, 0x090f, 0x0910, 0x090d, 0x0912, // a8
+ 0x0913, 0x0914, 0x0911, 0x0915, 0x0916, 0x0917, 0x0918, 0x0919, // b0
+ 0x091a, 0x091b, 0x091c, 0x091d, 0x091e, 0x091f, 0x0920, 0x0921, // b8
+ 0x0922, 0x0923, 0x0924, 0x0925, 0x0926, 0x0927, 0x0928, 0x0929, // c0
+ 0x092a, 0x092b, 0x092c, 0x092d, 0x092e, 0x092f, 0x095f, 0x0930, // c8
+ 0x0931, 0x0932, 0x0933, 0x0934, 0x0935, 0x0936, 0x0937, 0x0938, // d0
+ 0x0939, 0x0020, 0x093e, 0x093f, 0x0940, 0x0941, 0x0942, 0x0943, // d8
+ 0x0946, 0x0947, 0x0948, 0x0945, 0x094a, 0x094b, 0x094c, 0x0949, // e0
+ 0x094d, 0x093c, 0x0964, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, // e8
+ 0x0020, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, // f0
+ 0x0037, 0x0038, 0x0039, 0x20, 0x20, 0x20, 0x20, 0x20, // f8
};
const unsigned short *charsets[N_CHARSETS] = {
@@ -291,7 +298,8 @@ const unsigned short *charsets[N_CHARSETS] = {
ISO_8859_1,
ISO_8859_1,
KOI8_R, // 18
- ISCII };
+ ISCII
+};
// Tables of the relative lengths of vowels, depending on the
// type of the two phonemes that follow
@@ -299,7 +307,7 @@ const unsigned short *charsets[N_CHARSETS] = {
// use this table if vowel is not the last in the word
static unsigned char length_mods_en[100] = {
-/* a , t s n d z r N <- next */
+ /* a , t s n d z r N <- next */
100,120,100,105,100,110,110,100, 95, 100, /* a <- next2 */
105,120,105,110,125,130,135,115,125, 100, /* , */
105,120, 75,100, 75,105,120, 85, 75, 100, /* t */
@@ -309,11 +317,12 @@ static unsigned char length_mods_en[100] = {
105,120,100,105,105,122,125,110,105, 100, /* z */
105,120,100,105,105,122,125,110,105, 100, /* r */
105,120, 95,105,100,115,120,110,100, 100, /* N */
- 100,120,100,100,100,100,100,100,100, 100 }; // SPARE
+ 100,120,100,100,100,100,100,100,100, 100
+}; // SPARE
// as above, but for the last syllable in a word
static unsigned char length_mods_en0[100] = {
-/* a , t s n d z r N <- next */
+ /* a , t s n d z r N <- next */
100,150,100,105,110,115,110,110,110, 100, /* a <- next2 */
105,150,105,110,125,135,140,115,135, 100, /* , */
105,150, 90,105, 90,122,135,100, 90, 100, /* t */
@@ -323,31 +332,33 @@ static unsigned char length_mods_en0[100] = {
105,150,100,105,110,122,125,115,110, 100, /* z */
105,150,100,105,105,122,135,120,105, 100, /* r */
105,150,100,105,105,115,135,110,105, 100, /* N */
- 100,100,100,100,100,100,100,100,100, 100 }; // SPARE
+ 100,100,100,100,100,100,100,100,100, 100
+}; // SPARE
static unsigned char length_mods_equal[100] = {
-/* a , t s n d z r N <- next */
- 110,110,110,110,110,110,110,110,110, 110, /* a <- next2 */
- 110,110,110,110,110,110,110,110,110, 110, /* , */
- 110,110,110,110,110,110,110,110,110, 110, /* t */
- 110,110,110,110,110,110,110,110,110, 110, /* s */
- 110,110,110,110,110,110,110,110,110, 110, /* n */
- 110,110,110,110,110,110,110,110,110, 110, /* d */
- 110,110,110,110,110,110,110,110,110, 110, /* z */
- 110,110,110,110,110,110,110,110,110, 110, /* r */
- 110,110,110,110,110,110,110,110,110, 110, /* N */
- 110,110,110,110,110,110,110,110,110, 110 }; // SPARE
+ /* a , t s n d z r N <- next */
+ 110,120,100,110,110,110,110,110,110, 110, /* a <- next2 */
+ 110,120,100,110,110,110,110,110,110, 110, /* , */
+ 110,120,100,110,100,110,110,110,100, 110, /* t */
+ 110,120,100,110,110,110,110,110,110, 110, /* s */
+ 110,120,100,110,110,110,110,110,110, 110, /* n */
+ 110,120,100,110,110,110,110,110,110, 110, /* d */
+ 110,120,100,110,110,110,110,110,110, 110, /* z */
+ 110,120,100,110,110,110,110,110,110, 110, /* r */
+ 110,120,100,110,110,110,110,110,110, 110, /* N */
+ 110,120,100,110,110,110,110,110,110, 110
+}; // SPARE
static unsigned char *length_mod_tabs[6] = {
- length_mods_en,
- length_mods_en, // 1
- length_mods_en0, // 2
- length_mods_equal, // 3
- length_mods_equal, // 4
- length_mods_equal // 5
- };
+ length_mods_en,
+ length_mods_en, // 1
+ length_mods_en0, // 2
+ length_mods_equal, // 3
+ length_mods_equal, // 4
+ length_mods_equal // 5
+};
void SetLengthMods(Translator *tr, int value)
@@ -362,35 +373,67 @@ void SetLengthMods(Translator *tr, int value)
}
+
int IsAlpha(unsigned int c)
{//========================
// Replacement for iswalph() which also checks for some in-word symbols
- if(iswalpha(c))
+ static const unsigned short extra_indic_alphas[] = {
+ 0xa70,0xa71, // Gurmukhi: tippi, addak
+ 0
+ };
+
+ if(iswalpha2(c))
return(1);
+ if(c < 0x300)
+ return(0);
+
if((c >= 0x901) && (c <= 0xdf7))
{
// Indic scripts: Devanagari, Tamil, etc
if((c & 0x7f) < 0x64)
return(1);
+ if(lookupwchar(extra_indic_alphas, c) != 0)
+ return(1);
+ if((c >= 0xd7a) && (c <= 0xd7f))
+ return(1); // malaytalam chillu characters
+
return(0);
}
+ if((c >= 0x5b0) && (c <= 0x5c2))
+ return(1); // Hebrew vowel marks
+
+ if(c == 0x0605)
+ return(1);
+
+ if((c == 0x670) || ((c >= 0x64b) && (c <= 0x65e)))
+ return(1); // arabic vowel marks
+
if((c >= 0x300) && (c <= 0x36f))
return(1); // combining accents
+ if((c >= 0x780) && (c <= 0x7b1))
+ return(1); // taani/divehi (maldives)
+
+ if((c >= 0xf40) && (c <= 0xfbc))
+ return(1); // tibetan
+
if((c >= 0x1100) && (c <= 0x11ff))
return(1); //Korean jamo
+ if((c >= 0x2800) && (c <= 0x28ff))
+ return(1); // braille
+
if((c > 0x3040) && (c <= 0xa700))
return(1); // Chinese/Japanese. Should never get here, but Mac OS 10.4's iswalpha seems to be broken, so just make sure
return(0);
}
-static int IsDigit09(unsigned int c)
-{//=========================
+int IsDigit09(unsigned int c)
+{//============================
if((c >= '0') && (c <= '9'))
return(1);
return(0);
@@ -407,12 +450,16 @@ int IsDigit(unsigned int c)
return(0);
}
-int IsSpace(unsigned int c)
+static int IsSpace(unsigned int c)
{//========================
if(c == 0)
return(0);
- if(wcschr(chars_space,c))
- return(1);
+ if((c >= 0x2500) && (c < 0x25a0))
+ return(1); // box drawing characters
+ if((c >= 0xfff9) && (c <= 0xffff))
+ return(1); // unicode specials
+// if(wcschr(chars_space,c))
+// return(1);
return(iswspace(c));
}
@@ -438,6 +485,22 @@ int lookupwchar(const unsigned short *list,int c)
return(0);
}
+
+int lookupwchar2(const unsigned short *list,int c)
+{//==============================================
+// Replace character c by another character.
+// Returns 0 = not found, 1 = delete character
+ int ix;
+
+ for(ix=0; list[ix] != 0; ix+=2)
+ {
+ if(list[ix] == c)
+ return(list[ix+1]);
+ }
+ return(0);
+}
+
+
int IsBracket(int c)
{//=================
if((c >= 0x2014) && (c <= 0x201f))
@@ -467,8 +530,7 @@ int utf8_out(unsigned int c, char *buf)
}
if(c < 0x0800)
n_bytes = 1;
- else
- if(c < 0x10000)
+ else if(c < 0x10000)
n_bytes = 2;
else
n_bytes = 3;
@@ -500,7 +562,7 @@ int utf8_nbytes(const char *buf)
int utf8_in2(int *c, const char *buf, int backwards)
{//=================================================
-// Read a unicode characater from a UTF8 string
+// Read a unicode characater from a UTF8 string
// Returns the number of UTF8 bytes used.
// backwards: set if we are moving backwards through the UTF8 string
int c1;
@@ -524,11 +586,9 @@ int utf8_in2(int *c, const char *buf, int backwards)
{
if((c1 & 0xe0) == 0xc0)
n_bytes = 1;
- else
- if((c1 & 0xf0) == 0xe0)
+ else if((c1 & 0xf0) == 0xe0)
n_bytes = 2;
- else
- if((c1 & 0xf8) == 0xf0)
+ else if((c1 & 0xf8) == 0xf0)
n_bytes = 3;
c1 &= mask[n_bytes];
@@ -544,7 +604,7 @@ int utf8_in2(int *c, const char *buf, int backwards)
int utf8_in(int *c, const char *buf)
{//=================================
-// Read a unicode characater from a UTF8 string
+// Read a unicode characater from a UTF8 string
// Returns the number of UTF8 bytes used.
return(utf8_in2(c,buf,0));
}
@@ -558,45 +618,250 @@ char *strchr_w(const char *s, int c)
return(strchr((char *)s,c)); // (char *) is needed for Borland compiler
}
+#if 0
+static int IsAllUpper(const char *word)
+{//=============================
+ int c;
+ while((*word != 0) && !isspace2(*word))
+ {
+ word += utf8_in(&c, word);
+ if(!iswupper2(c))
+ return(0);
+ }
+ return(1);
+}
+#endif
+
+static char *SpeakIndividualLetters(Translator *tr, char *word, char *phonemes, int spell_word)
+{//============================================================================================
+ int posn = 0;
+ int capitals = 0;
+ int non_initial = 0;
+
+ if(spell_word > 2)
+ capitals = 2; // speak 'capital'
+ if(spell_word > 1)
+ capitals |= 4; // speak charater code for unknown letters
+
+ while((*word != ' ') && (*word != 0))
+ {
+ word += TranslateLetter(tr, word, phonemes, capitals | non_initial);
+ posn++;
+ non_initial = 1;
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ strcpy(word_phonemes,phonemes);
+ return(NULL);
+ }
+ }
+ SetSpellingStress(tr,phonemes,spell_word,posn);
+ return(word);
+} // end of SpeakIndividualLetters
+
+
+
+static int CheckDottedAbbrev(char *word1, WORD_TAB *wtab)
+{//=====================================================
+ int wc;
+ int count = 0;
+ int nbytes;
+ int ok;
+ int ix;
+ char *word;
+ char *wbuf;
+ char word_buf[80];
+
+ word = word1;
+ wbuf = word_buf;
+ ix = 0;
+
+ for(;;)
+ {
+ ok = 0;
+ nbytes = utf8_in(&wc, word);
+ if((word[nbytes] == ' ') && IsAlpha(wc))
+ {
+ if(word[nbytes+1] == '.')
+ {
+ if(word[nbytes+2] == ' ')
+ ok = 1;
+ else if(word[nbytes+2] =='\'')
+ {
+ nbytes += 2; // delete the final dot (eg. u.s.a.'s)
+ ok = 2;
+ }
+ }
+ else if((count > 0) && (word[nbytes] == ' '))
+ ok = 2;
+ }
+
+ if(ok == 0)
+ break;
+
+ for(ix=0; ix < nbytes; ix++)
+ *wbuf++ = word[ix];
+
+ count++;
+
+ if(ok == 2)
+ {
+ word += nbytes;
+ break;
+ }
+
+ word += (nbytes + 3);
+ }
+
+ if(count > 1)
+ {
+ ix = wbuf - word_buf;
+ memcpy(word1, word_buf, ix);
+ while(&word1[ix] < word)
+ word1[ix++] = ' ';
+ dictionary_skipwords = (count - 1)*2;
+ }
+ return(count);
+} // end of CheckDottedAbbrev
+
+
+extern char *phondata_ptr;
+
+static int ChangeEquivalentPhonemes(Translator *tr, int lang2, char *phonemes)
+{//====================================================================
+// tr: the original language
+// lang2: phoneme table number for the temporary language
+// phonemes: the phonemes to be replaced
+
+ int ix;
+ int len;
+ char phon;
+ char *p;
+ unsigned char *pb;
+ char *eqlist;
+ char *p_out;
+ char *p_in;
+ int remove_stress = 0;
+ char phonbuf[N_WORD_PHONEMES];
+
+ // has a phoneme equivalence table been specified for thus language pair?
+ if((ix = phoneme_tab_list[tr->phoneme_tab_ix].equivalence_tables) == 0)
+ return(0);
+
+ pb = (unsigned char *)&phondata_ptr[ix];
+
+ for(;;)
+ {
+ if(pb[0] == 0)
+ return(0); // table not found
+
+ if(pb[0] == lang2)
+ break;
+
+ len = (pb[2] << 8) + pb[3]; // size of this table in words
+ pb += (len * 4);
+ }
+ remove_stress = pb[1];
+
+ if(option_phonemes == 2)
+ {
+ DecodePhonemes(phonemes, phonbuf);
+ fprintf(f_trans,"(%s) %s -> (%s) ", phoneme_tab_list[lang2].name, phonbuf, phoneme_tab_list[tr->phoneme_tab_ix].name);
+ }
+
+ p_in = phonemes;
+ eqlist = (char *)&pb[8];
+ p_out = phonbuf;
+
+ while((phon = *p_in++) != 0)
+ {
+ if(remove_stress && ((phon & 0xff) < phonSTRESS_PREV))
+ continue; // remove stress marks
+
+ // is there a translation for this phoneme code?
+ p = eqlist;
+ while(*p != 0)
+ {
+ len = strlen(&p[1]);
+ if(*p == phon)
+ {
+ strcpy(p_out, &p[1]);
+ p_out += len;
+ break;
+ }
+ p += (len + 2);
+ }
+ if(*p == 0)
+ {
+ // no translation found
+ *p_out++ = phon;
+ }
+ }
+ *p_out = 0;
+
+ if(remove_stress)
+ {
+ SetWordStress(tr, phonbuf, NULL, -1, 0);
+ }
+
+ strcpy(phonemes, phonbuf);
+
+ if(option_phonemes == 2)
+ {
+ SelectPhonemeTable(tr->phoneme_tab_ix);
+ DecodePhonemes(phonemes, phonbuf);
+ fprintf(f_trans,"%s\n\n", phonbuf);
+ }
+ return(1);
+} // end of ChangeEquivalentPhonemes
+
-int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab)
-{//===========================================================================
+int TranslateWord(Translator *tr, char *word_start, int next_pause, WORD_TAB *wtab, char *word_out)
+{//==================================================================================================
// word1 is terminated by space (0x20) character
- int length;
+ char *word1;
int word_length;
int ix;
- int posn;
+ char *p;
int pfix;
int n_chars;
unsigned int dictionary_flags[2];
unsigned int dictionary_flags2[2];
int end_type=0;
+ int end_type1=0;
int prefix_type=0;
+ int prefix_stress;
char *wordx;
char phonemes[N_WORD_PHONEMES];
- char *ph_limit;
- char *phonemes_ptr;
+ char phonemes2[N_WORD_PHONEMES];
char prefix_phonemes[N_WORD_PHONEMES];
+ char unpron_phonemes[N_WORD_PHONEMES];
char end_phonemes[N_WORD_PHONEMES];
+ char end_phonemes2[N_WORD_PHONEMES];
char word_copy[N_WORD_BYTES];
- char prefix_chars[N_WORD_BYTES];
+ char word_copy2[N_WORD_BYTES];
+ int word_copy_length;
+ char prefix_chars[0x3f + 2];
int found=0;
- int end_flags;
- char c_temp; // save a character byte while we temporarily replace it with space
+ int end_flags;
+ int c_temp; // save a character byte while we temporarily replace it with space
int first_char;
int last_char = 0;
- int unpron_length;
int add_plural_suffix = 0;
int prefix_flags = 0;
+ int more_suffixes;
int confirm_prefix;
int spell_word;
int stress_bits;
int emphasize_allcaps = 0;
- int wflags = wtab->flags;
- int wmark = wtab->wmark;
+ int wflags;
+ int wmark;
+ int was_unpronouncable = 0;
+ int loopcount;
+ WORD_TAB wtab_null[8];
// translate these to get pronunciations of plural 's' suffix (different forms depending on
// the preceding letter
@@ -604,18 +869,37 @@ int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab)
static char word_iz[4] = {0,'i','z',0};
static char word_ss[4] = {0,'s','s',0};
+ if(wtab == NULL)
+ {
+ memset(wtab_null, 0, sizeof(wtab_null));
+ wtab = wtab_null;
+ }
+ wflags = wtab->flags;
+ wmark = wtab->wmark;
+
dictionary_flags[0] = 0;
dictionary_flags[1] = 0;
dictionary_flags2[0] = 0;
dictionary_flags2[1] = 0;
dictionary_skipwords = 0;
+ phonemes[0] = 0;
+ unpron_phonemes[0] = 0;
prefix_phonemes[0] = 0;
end_phonemes[0] = 0;
- ph_limit = &phonemes[N_WORD_PHONEMES];
+
+ if(tr->data_dictlist == NULL)
+ {
+ // dictionary is not loaded
+ word_phonemes[0] = 0;
+ return(0);
+ }
// count the length of the word
+ word1 = word_start;
+ if(*word1 == ' ') word1++; // possibly a dot was replaced by space: $dot
wordx = word1;
+
utf8_in(&first_char,wordx);
word_length = 0;
while((*wordx != 0) && (*wordx != ' '))
@@ -624,15 +908,36 @@ int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab)
word_length++;
}
- // try an initial lookup in the dictionary list, we may find a pronunciation specified, or
- // we may just find some flags
+ word_copy_length = wordx - word_start;
+ if(word_copy_length >= N_WORD_BYTES)
+ word_copy_length = N_WORD_BYTES-1;
+ memcpy(word_copy2, word_start, word_copy_length);
+
spell_word = 0;
+
+ if((word_length == 1) && (wflags & FLAG_TRANSLATOR2))
+ {
+ // retranslating a 1-character word using a different language, say its name
+ utf8_in(&c_temp, wordx+1); // the next character
+ if(!IsAlpha(c_temp) || (AlphabetFromChar(last_char) != AlphabetFromChar(c_temp)))
+ spell_word = 1;
+ }
+
if(option_sayas == SAYAS_KEY)
{
if(word_length == 1)
spell_word = 4;
+ else
+ {
+ // is there a translation for this keyname ?
+ word1--;
+ *word1 = '_'; // prefix keyname with '_'
+ found = LookupDictList(tr, &word1, phonemes, dictionary_flags, 0, wtab);
+ }
}
+ // try an initial lookup in the dictionary list, we may find a pronunciation specified, or
+ // we may just find some flags
if(option_sayas & 0x10)
{
// SAYAS_CHAR, SAYAS_GYLPH, or SAYAS_SINGLE_CHAR
@@ -640,9 +945,20 @@ int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab)
}
else
{
- found = LookupDictList(tr, &word1, phonemes, dictionary_flags, FLAG_ALLOW_TEXTMODE, wtab); // the original word
+ if(!found)
+ found = LookupDictList(tr, &word1, phonemes, dictionary_flags, FLAG_ALLOW_TEXTMODE, wtab); // the original word
+
+
+ if((dictionary_flags[0] & (FLAG_ALLOW_DOT || FLAG_NEEDS_DOT)) && (wordx[1] == '.'))
+ {
+ wordx[1] = ' '; // remove a Dot after this word
+ }
+
if(dictionary_flags[0] & FLAG_TEXTMODE)
{
+ if(word_out != NULL)
+ strcpy(word_out, word1);
+
first_char = word1[0];
stress_bits = dictionary_flags[0] & 0x7f;
found = LookupDictList(tr, &word1, phonemes, dictionary_flags2, 0, wtab); // the text replacement
@@ -657,8 +973,7 @@ int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab)
}
}
}
- else
- if((found==0) && (dictionary_flags[0] & FLAG_SKIPWORDS))
+ else if((found==0) && (dictionary_flags[0] & FLAG_SKIPWORDS) && !(dictionary_flags[0] & FLAG_ABBREV))
{
// grouped words, but no translation. Join the words with hyphens.
wordx = word1;
@@ -674,6 +989,20 @@ int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab)
}
}
+ if((word_length == 1) && (dictionary_skipwords == 0))
+ {
+ // is this a series of single letters separated by dots?
+ if(CheckDottedAbbrev(word1, wtab))
+ {
+ dictionary_flags[0] = 0;
+ dictionary_flags[1] = 0;
+ spell_word = 1;
+ if(dictionary_skipwords)
+ dictionary_flags[0] = FLAG_SKIPWORDS;
+ }
+ }
+
+
// if textmode, LookupDictList() replaces word1 by the new text and returns found=0
if(phonemes[0] == phonSWITCH)
@@ -683,48 +1012,58 @@ int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab)
return(0);
}
-if((wmark > 0) && (wmark < 8))
-{
- // the stressed syllable has been specified in the text (TESTING)
- dictionary_flags[0] = (dictionary_flags[0] & ~0xf) | wmark;
-}
+ if((wmark > 0) && (wmark < 8))
+ {
+ // the stressed syllable has been specified in the text (TESTING)
+ dictionary_flags[0] = (dictionary_flags[0] & ~0xf) | wmark;
+ }
if(!found && (dictionary_flags[0] & FLAG_ABBREV))
{
// the word has $abbrev flag, but no pronunciation specified. Speak as individual letters
spell_word = 1;
}
-
+
if(!found && iswdigit(first_char))
{
Lookup(tr,"_0lang",word_phonemes);
if(word_phonemes[0] == phonSWITCH)
return(0);
- found = TranslateNumber(tr,word1,phonemes,dictionary_flags,wflags);
+ if((tr->langopts.numbers2 & NUM2_ENGLISH_NUMERALS) && !(wtab->flags & FLAG_CHAR_REPLACED))
+ {
+ // for this language, speak English numerals (0-9) with the English voice
+ sprintf(word_phonemes,"%c",phonSWITCH);
+ return(0);
+ }
+
+ found = TranslateNumber(tr, word1, phonemes, dictionary_flags, wtab, 0);
}
- if(!found & ((wflags & FLAG_UPPERS) != FLAG_FIRST_UPPER))
+ if(!found && ((wflags & FLAG_UPPERS) != FLAG_FIRST_UPPER))
{
// either all upper or all lower case
- if((tr->langopts.numbers & NUM_ROMAN) || ((tr->langopts.numbers & NUM_ROMAN_UC) && (wflags & FLAG_ALL_UPPER)))
+ if((tr->langopts.numbers & NUM_ROMAN) || ((tr->langopts.numbers & NUM_ROMAN_CAPITALS) && (wflags & FLAG_ALL_UPPER)))
{
- if((found = TranslateRoman(tr, word1, phonemes)) != 0)
- dictionary_flags[0] |= FLAG_ABBREV; // prevent emphasis if capitals
+ if((wflags & FLAG_LAST_WORD) || !(wtab[1].flags & FLAG_NOSPACE))
+ {
+ // don't use Roman number if this word is not separated from the next word (eg. "XLTest")
+ if((found = TranslateRoman(tr, word1, phonemes, wtab)) != 0)
+ dictionary_flags[0] |= FLAG_ABBREV; // prevent emphasis if capitals
+ }
}
}
- if((wflags & FLAG_ALL_UPPER) && (word_length > 1)&& iswalpha(first_char))
+ if((wflags & FLAG_ALL_UPPER) && (word_length > 1)&& iswalpha2(first_char))
{
if((option_tone_flags & OPTION_EMPHASIZE_ALLCAPS) && !(dictionary_flags[0] & FLAG_ABBREV))
{
// emphasize words which are in capitals
emphasize_allcaps = FLAG_EMPHASIZED;
}
- else
- if(!found && !(dictionary_flags[0] & FLAG_SKIPWORDS) && (word_length<4) && (tr->clause_lower_count > 3)
- && (tr->clause_upper_count <= tr->clause_lower_count))
+ else if(!found && !(dictionary_flags[0] & FLAG_SKIPWORDS) && (word_length<4) && (tr->clause_lower_count > 3)
+ && (tr->clause_upper_count <= tr->clause_lower_count))
{
// An upper case word in a lower case clause. This could be an abbreviation.
spell_word = 1;
@@ -735,53 +1074,62 @@ if((wmark > 0) && (wmark < 8))
if(spell_word > 0)
{
// Speak as individual letters
- wordx = word1;
- posn = 0;
phonemes[0] = 0;
end_type = 0;
- while(*wordx != ' ')
+ if(SpeakIndividualLetters(tr, word1, phonemes, spell_word) == NULL)
{
- wordx += TranslateLetter(tr,wordx, phonemes,spell_word, word_length);
- posn++;
+ if(word_length > 1)
+ return(FLAG_SPELLWORD); // a mixture of languages, retranslate as individual letters, separated by spaces
if(phonemes[0] == phonSWITCH)
{
- // change to another language in order to translate this word
- strcpy(word_phonemes,phonemes);
- if(word_length > 1)
- return(FLAG_SPELLWORD); // a mixture of languages, retranslate as individual letters, separated by spaces
- return(0);
+// problem with espeak -vbg "b.c.d.e.f"
}
+ return(0);
}
- SetSpellingStress(tr,phonemes,spell_word,posn);
+ strcpy(word_phonemes, phonemes);
+ if(wflags & FLAG_TRANSLATOR2)
+ return(0);
+ return(dictionary_flags[0] & FLAG_SKIPWORDS); // for "b.c.d"
}
- else
- if(found == 0)
+ else if(found == 0)
{
+ int posn;
+ int non_initial;
+ int length;
// word's pronunciation is not given in the dictionary list, although
// dictionary_flags may have ben set there
posn = 0;
+ non_initial = 0;
length = 999;
wordx = word1;
- while(((length < 3) && (length > 0))|| (word_length > 1 && Unpronouncable(tr,wordx)))
+ while(((length < 3) && (length > 0))|| (word_length > 1 && Unpronouncable(tr, wordx, posn)))
{
- char *p;
// This word looks "unpronouncable", so speak letters individually until we
// find a remainder that we can pronounce.
+ was_unpronouncable = FLAG_WAS_UNPRONOUNCABLE;
emphasize_allcaps = 0;
- wordx += TranslateLetter(tr,wordx,phonemes,0, word_length);
+
+ if(wordx[0] == '\'')
+ break;
+
+ if(posn > 0)
+ non_initial = 1;
+
+ wordx += TranslateLetter(tr, wordx, unpron_phonemes, non_initial);
posn++;
- if(phonemes[0] == phonSWITCH)
+ if(unpron_phonemes[0] == phonSWITCH)
{
// change to another language in order to translate this word
- strcpy(word_phonemes,phonemes);
- if(strcmp(&phonemes[1],"en")==0)
- return(FLAG_SPELLWORD); // _^_en must have been set in TranslateLetter(), not *_rules
+ strcpy(word_phonemes,unpron_phonemes);
+ if(strcmp(&unpron_phonemes[1],"en")==0)
+ return(FLAG_SPELLWORD); // _^_en must have been set in TranslateLetter(), not *_rules which uses only _^_
return(0);
}
+#ifdef deleted
p = &wordx[word_length-3]; // this looks wrong. Doesn't consider multi-byte chars.
if(memcmp(p,"'s ",3) == 0)
{
@@ -791,19 +1139,22 @@ if((wmark > 0) && (wmark < 8))
p[1] = ' ';
last_char = p[-1];
}
-
+#endif
length=0;
while(wordx[length] != ' ') length++;
- if(length > 0)
- wordx[-1] = ' '; // prevent this affecting the pronunciation of the pronuncable part
}
- SetSpellingStress(tr,phonemes,0,posn);
+ SetSpellingStress(tr,unpron_phonemes,0,posn);
// anything left ?
if(*wordx != ' ')
{
+ if((unpron_phonemes[0] != 0) && (wordx[0] != '\''))
+ {
+ // letters which have been spoken individually from affecting the pronunciation of the pronuncable part
+ wordx[-1] = ' ';
+ }
+
// Translate the stem
- unpron_length = strlen(phonemes);
end_type = TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, end_phonemes, wflags, dictionary_flags);
if(phonemes[0] == phonSWITCH)
@@ -813,27 +1164,31 @@ if((wmark > 0) && (wmark < 8))
return(0);
}
+#ifdef deleted
+// ?? allow $unpr while translating rules, not just on initial FLAG_UNPRON_TEST
+if(end_type & SUFX_UNPRON)
+{
+ phonemes[0] = 0; // discard and retranslate as individual letters
+ SpeakIndividualLetters(tr, wordx, phonemes, 0);
+ strcpy(word_phonemes, phonemes);
+ return(0);
+}
+#endif
+
if((phonemes[0] == 0) && (end_phonemes[0] == 0))
{
int wc;
// characters not recognised, speak them individually
-
+ // ?? should we say super/sub-script numbers and letters here?
utf8_in(&wc, wordx);
- if((word_length == 1) && IsAlpha(wc))
+ if((word_length == 1) && (IsAlpha(wc) || IsSuperscript(wc)))
{
- posn = 0;
- while((*wordx != ' ') && (*wordx != 0))
+ if((wordx = SpeakIndividualLetters(tr, wordx, phonemes, spell_word)) == NULL)
{
- wordx += TranslateLetter(tr,wordx, phonemes, 4, word_length);
- posn++;
- if(phonemes[0] == phonSWITCH)
- {
- // change to another language in order to translate this word
- strcpy(word_phonemes,phonemes);
- return(0);
- }
+ return(0);
}
- SetSpellingStress(tr,phonemes,spell_word,posn);
+ strcpy(word_phonemes, phonemes);
+ return(0);
}
}
@@ -841,14 +1196,13 @@ if((wmark > 0) && (wmark < 8))
found = 0;
confirm_prefix = 1;
- while(end_type & SUFX_P)
+ for (loopcount = 0; (loopcount < 50) && (end_type & SUFX_P); loopcount++)
{
// Found a standard prefix, remove it and retranslate
-
+ // loopcount guards against an endless loop
if(confirm_prefix && !(end_type & SUFX_B))
{
int end2;
- char phonemes2[N_WORD_PHONEMES];
char end_phonemes2[N_WORD_PHONEMES];
// remove any standard suffix and confirm that the prefix is still recognised
@@ -903,7 +1257,7 @@ if((wmark > 0) && (wmark < 8))
for(ix=0; ix < n_chars; ix++) // num. of bytes to remove
{
prefix_chars[pfix++] = *wordx++;
-
+
if((prefix_type & SUFX_B) && (ix == (n_chars-1)))
{
prefix_chars[pfix-1] = 0; // discard the last character of the prefix, this is the separator character
@@ -914,23 +1268,30 @@ if((wmark > 0) && (wmark < 8))
c_temp = wordx[-1];
wordx[-1] = ' ';
confirm_prefix = 1;
+ wflags |= FLAG_PREFIX_REMOVED;
if(prefix_type & SUFX_B)
{
-// SUFX_B is used for Turkish, tr_rules contains "(Pb£
- // retranslate the prefix part
+// SUFX_B is used for Turkish, tr_rules contains " ' (Pb"
+ // examine the prefix part
char *wordpf;
char prefix_phonemes2[12];
strncpy0(prefix_phonemes2,end_phonemes,sizeof(prefix_phonemes2));
wordpf = &prefix_chars[1];
- found = LookupDictList(tr, &wordpf, phonemes, dictionary_flags, SUFX_P, wtab); // without prefix
- if(found == 0)
+ strcpy(prefix_phonemes, phonemes);
+
+ // look for stress marker or $abbrev
+ found = LookupDictList(tr, &wordpf, phonemes, dictionary_flags, 0, wtab);
+ if(found)
{
- end_type = TranslateRules(tr, wordpf, phonemes, N_WORD_PHONEMES, end_phonemes, 0, dictionary_flags);
- sprintf(prefix_phonemes,"%s%s%s",phonemes,end_phonemes,prefix_phonemes2);
+ strcpy(prefix_phonemes, phonemes);
+ }
+ if(dictionary_flags[0] & FLAG_ABBREV)
+ {
+ prefix_phonemes[0] = 0;
+ SpeakIndividualLetters(tr, wordpf, prefix_phonemes, 1);
}
- prefix_flags = 1;
}
else
{
@@ -949,7 +1310,7 @@ if((wmark > 0) && (wmark < 8))
prefix_flags = 1;
if(found == 0)
{
- end_type = TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, end_phonemes, 0, dictionary_flags);
+ end_type = TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, end_phonemes, wflags & (FLAG_HYPHEN_AFTER | FLAG_PREFIX_REMOVED), dictionary_flags);
if(phonemes[0] == phonSWITCH)
{
@@ -961,85 +1322,129 @@ if((wmark > 0) && (wmark < 8))
}
}
+
+
+
if((end_type != 0) && !(end_type & SUFX_P))
{
-char phonemes2[N_WORD_PHONEMES];
-strcpy(phonemes2,phonemes);
+ end_type1 = end_type;
+ strcpy(phonemes2,phonemes);
// The word has a standard ending, re-translate without this ending
end_flags = RemoveEnding(tr, wordx, end_type, word_copy);
+ more_suffixes = 1;
- phonemes_ptr = &phonemes[unpron_length];
- phonemes_ptr[0] = 0;
-
- if(prefix_phonemes[0] != 0)
+ while(more_suffixes)
{
- // lookup the stem without the prefix removed
- wordx[-1] = c_temp;
- found = LookupDictList(tr, &word1, phonemes_ptr, dictionary_flags2, end_flags, wtab); // include prefix, but not suffix
- wordx[-1] = ' ';
- if(dictionary_flags[0]==0)
- {
- dictionary_flags[0] = dictionary_flags2[0];
- dictionary_flags[1] = dictionary_flags2[1];
- }
- if(found)
- prefix_phonemes[0] = 0; // matched whole word, don't need prefix now
+ more_suffixes = 0;
+ phonemes[0] = 0;
- if((found==0) && (dictionary_flags2[0] != 0))
- prefix_flags = 1;
- }
- if(found == 0)
- {
- found = LookupDictList(tr, &wordx, phonemes_ptr, dictionary_flags2, end_flags, wtab); // without prefix and suffix
- if(phonemes_ptr[0] == phonSWITCH)
+ if(prefix_phonemes[0] != 0)
{
- // change to another language in order to translate this word
- memcpy(wordx,word_copy,strlen(word_copy));
- strcpy(word_phonemes,phonemes_ptr);
- return(0);
- }
- if(dictionary_flags[0]==0)
- {
- dictionary_flags[0] = dictionary_flags2[0];
- dictionary_flags[1] = dictionary_flags2[1];
- }
- }
- if(found == 0)
- {
- if(end_type & SUFX_Q)
- {
- // don't retranslate, use the original lookup result
- strcpy(phonemes,phonemes2);
+ // lookup the stem without the prefix removed
+ wordx[-1] = c_temp;
+ found = LookupDictList(tr, &word1, phonemes, dictionary_flags2, end_flags, wtab); // include prefix, but not suffix
+ wordx[-1] = ' ';
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ memcpy(wordx,word_copy,strlen(word_copy));
+ strcpy(word_phonemes,phonemes);
+ return(0);
+ }
+ if(dictionary_flags[0]==0)
+ {
+ dictionary_flags[0] = dictionary_flags2[0];
+ dictionary_flags[1] = dictionary_flags2[1];
+ }
+ if(found)
+ prefix_phonemes[0] = 0; // matched whole word, don't need prefix now
- // language specific changes
- ApplySpecialAttribute(tr,phonemes,dictionary_flags[0]);
+ if((found==0) && (dictionary_flags2[0] != 0))
+ prefix_flags = 1;
}
- else
+ if(found == 0)
{
- if(end_flags & FLAG_SUFX)
- TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, NULL,wflags | FLAG_SUFFIX_REMOVED, dictionary_flags);
- else
- TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, NULL,wflags,dictionary_flags);
-
+ found = LookupDictList(tr, &wordx, phonemes, dictionary_flags2, end_flags, wtab); // without prefix and suffix
if(phonemes[0] == phonSWITCH)
{
// change to another language in order to translate this word
- strcpy(word_phonemes,phonemes);
memcpy(wordx,word_copy,strlen(word_copy));
- wordx[-1] = c_temp;
+ strcpy(word_phonemes,phonemes);
return(0);
}
+
+ if(dictionary_flags2[0] & FLAG_ABBREV)
+ {
+ // Removing the suffix leaves a word which should be spoken as individual letters
+ // Not yet implemented
+ }
+ if(dictionary_flags[0]==0)
+ {
+ dictionary_flags[0] = dictionary_flags2[0];
+ dictionary_flags[1] = dictionary_flags2[1];
+ }
+ }
+ if(found == 0)
+ {
+ if(end_type & SUFX_Q)
+ {
+ // don't retranslate, use the original lookup result
+ strcpy(phonemes,phonemes2);
+ }
+ else
+ {
+ if(end_flags & FLAG_SUFX)
+ wflags |= FLAG_SUFFIX_REMOVED;
+ if(end_type & SUFX_A)
+ wflags |= FLAG_SUFFIX_VOWEL;
+
+ if(end_type & SUFX_M)
+ {
+ // allow more suffixes before this suffix
+ strcpy(end_phonemes2, end_phonemes);
+ end_type = TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, end_phonemes, wflags, dictionary_flags);
+ strcat(end_phonemes, end_phonemes2); // add the phonemes for the previous suffixes after this one
+
+ if((end_type != 0) && !(end_type & SUFX_P))
+ {
+ // there is another suffix
+ end_flags = RemoveEnding(tr, wordx, end_type, NULL);
+ more_suffixes = 1;
+ }
+ }
+ else
+ {
+ // don't remove any previous suffix
+ TranslateRules(tr, wordx, phonemes, N_WORD_PHONEMES, NULL, wflags, dictionary_flags);
+ end_type = 0;
+ }
+
+ if(phonemes[0] == phonSWITCH)
+ {
+ // change to another language in order to translate this word
+ strcpy(word_phonemes,phonemes);
+ memcpy(wordx,word_copy,strlen(word_copy));
+ wordx[-1] = c_temp;
+ return(0);
+ }
+ }
}
}
- if((end_type & SUFX_T) == 0)
+
+ if((end_type1 & SUFX_T) == 0)
{
// the default is to add the suffix and then determine the word's stress pattern
AppendPhonemes(tr,phonemes, N_WORD_PHONEMES, end_phonemes);
end_phonemes[0] = 0;
}
+ memcpy(wordx,word_copy,strlen(word_copy));
}
+
+
+
+
wordx[-1] = c_temp;
}
}
@@ -1049,8 +1454,7 @@ strcpy(phonemes2,phonemes);
// s or 's suffix, append [s], [z] or [Iz] depending on previous letter
if(last_char == 'f')
TranslateRules(tr, &word_ss[1], phonemes, N_WORD_PHONEMES, NULL, 0, NULL);
- else
- if((last_char==0) || (strchr_w("hsx",last_char)==NULL))
+ else if((last_char==0) || (strchr_w("hsx",last_char)==NULL))
TranslateRules(tr, &word_zz[1], phonemes, N_WORD_PHONEMES, NULL, 0, NULL);
else
TranslateRules(tr, &word_iz[1], phonemes, N_WORD_PHONEMES, NULL, 0, NULL);
@@ -1061,15 +1465,21 @@ strcpy(phonemes2,phonemes);
/* determine stress pattern for this word */
/******************************************/
- /* NOTE: this also adds a single PAUSE if the previous word ended
- in a primary stress, and this one starts with one */
- if(prefix_flags || (strchr(prefix_phonemes,phonSTRESS_P)!=NULL))
+ prefix_stress = 0;
+ for(p = prefix_phonemes; *p != 0; p++)
+ {
+ if((*p == phonSTRESS_P) || (*p == phonSTRESS_P2))
+ {
+ prefix_stress = *p;
+ }
+ }
+ if(prefix_flags || (prefix_stress != 0))
{
if((tr->langopts.param[LOPT_PREFIXES]) || (prefix_type & SUFX_T))
{
char *p;
// German, keep a secondary stress on the stem
- SetWordStress(tr, phonemes, &dictionary_flags[0], 3, 0);
+ SetWordStress(tr, phonemes, dictionary_flags, 3, 0);
// reduce all but the first primary stress
ix=0;
@@ -1083,32 +1493,46 @@ strcpy(phonemes2,phonemes);
*p = phonSTRESS_3;
}
}
- strcpy(word_phonemes,prefix_phonemes);
- strcat(word_phonemes,phonemes);
- SetWordStress(tr, word_phonemes, &dictionary_flags[0], -1, 0);
+#ifdef PLATFORM_WINDOWS
+ sprintf(word_phonemes, "%s%s%s", unpron_phonemes, prefix_phonemes, phonemes);
+#else
+ snprintf(word_phonemes, sizeof(word_phonemes), "%s%s%s", unpron_phonemes, prefix_phonemes, phonemes);
+#endif
+ word_phonemes[N_WORD_PHONEMES-1] = 0;
+ SetWordStress(tr, word_phonemes, dictionary_flags, -1, 0);
}
else
{
// stress position affects the whole word, including prefix
- strcpy(word_phonemes,prefix_phonemes);
- strcat(word_phonemes,phonemes);
- SetWordStress(tr, word_phonemes, &dictionary_flags[0], -1, tr->prev_last_stress);
+#ifdef PLATFORM_WINDOWS
+ sprintf(word_phonemes, "%s%s%s", unpron_phonemes, prefix_phonemes, phonemes);
+#else
+ snprintf(word_phonemes, sizeof(word_phonemes), "%s%s%s", unpron_phonemes, prefix_phonemes, phonemes);
+#endif
+ word_phonemes[N_WORD_PHONEMES-1] = 0;
+ SetWordStress(tr, word_phonemes, dictionary_flags, -1, 0);
}
}
else
{
if(prefix_phonemes[0] == 0)
- SetWordStress(tr, phonemes, &dictionary_flags[0], -1, tr->prev_last_stress);
+ SetWordStress(tr, phonemes, dictionary_flags, -1, 0);
else
- SetWordStress(tr, phonemes, &dictionary_flags[0], -1, 0);
- strcpy(word_phonemes,prefix_phonemes);
- strcat(word_phonemes,phonemes);
+ SetWordStress(tr, phonemes, dictionary_flags, -1, 0);
+#ifdef PLATFORM_WINDOWS
+ sprintf(word_phonemes, "%s%s%s", unpron_phonemes, prefix_phonemes, phonemes);
+#else
+ snprintf(word_phonemes, sizeof(word_phonemes), "%s%s%s", unpron_phonemes, prefix_phonemes, phonemes);
+#endif
+ word_phonemes[N_WORD_PHONEMES-1] = 0;
}
if(end_phonemes[0] != 0)
{
// a suffix had the SUFX_T option set, add the suffix after the stress pattern has been determined
- strcat(word_phonemes,end_phonemes);
+ ix = strlen(word_phonemes);
+ end_phonemes[N_WORD_PHONEMES-1-ix] = 0; // ensure no buffer overflow
+ strcpy(&word_phonemes[ix], end_phonemes);
}
if(wflags & FLAG_LAST_WORD)
@@ -1118,7 +1542,26 @@ strcpy(phonemes2,phonemes);
dictionary_flags[0] &= ~FLAG_PAUSE1;
}
- if(wflags & FLAG_EMPHASIZED2)
+#ifdef deleted
+// but it causes problems if these are not a person name
+ if(tr->translator_name == L('h','u'))
+ {
+ // lang=hu, If the last two words of a clause have capital letters (eg. a person name), unstress the last word.
+ if((wflags & (FLAG_LAST_WORD | FLAG_FIRST_UPPER | FLAG_ALL_UPPER | FLAG_FIRST_WORD)) == (FLAG_LAST_WORD | FLAG_FIRST_UPPER))
+ {
+ if(((wtab[-1].flags & (FLAG_FIRST_UPPER | FLAG_ALL_UPPER)) == FLAG_FIRST_UPPER) && ((tr->clause_terminator != 0x90028) || (wflags & FLAG_HAS_DOT)))
+ {
+ ChangeWordStress(tr,word_phonemes,3);
+ }
+ }
+ }
+#endif
+
+ if((wflags & FLAG_HYPHEN) && (tr->langopts.stress_flags & S_HYPEN_UNSTRESS))
+ {
+ ChangeWordStress(tr,word_phonemes,3);
+ }
+ else if(wflags & FLAG_EMPHASIZED2)
{
// A word is indicated in the source text as stressed
// Give it stress level 6 (for the intonation module)
@@ -1127,20 +1570,19 @@ strcpy(phonemes2,phonemes);
if(wflags & FLAG_EMPHASIZED)
dictionary_flags[0] |= FLAG_PAUSE1; // precede by short pause
}
- else
- if(wtab[dictionary_skipwords].flags & FLAG_LAST_WORD)
+ else if(wtab[dictionary_skipwords].flags & FLAG_LAST_WORD)
{
// the word has attribute to stress or unstress when at end of clause
if(dictionary_flags[0] & (FLAG_STRESS_END | FLAG_STRESS_END2))
ChangeWordStress(tr,word_phonemes,4);
- else
- if(dictionary_flags[0] & FLAG_UNSTRESS_END)
+ else if((dictionary_flags[0] & FLAG_UNSTRESS_END) && (any_stressed_words))
ChangeWordStress(tr,word_phonemes,3);
}
+
// dictionary flags for this word give a clue about which alternative pronunciations of
// following words to use.
- if(end_type & SUFX_F)
+ if(end_type1 & SUFX_F)
{
// expect a verb form, with or without -s suffix
tr->expect_verb = 2;
@@ -1154,16 +1596,14 @@ strcpy(phonemes2,phonemes);
tr->expect_verb = 0;
tr->expect_noun = 0;
}
- else
- if(dictionary_flags[1] & FLAG_VERBF)
+ else if(dictionary_flags[1] & FLAG_VERBF)
{
/* expect a verb in the next word */
tr->expect_verb = 2;
tr->expect_verb_s = 0; /* verb won't have -s suffix */
tr->expect_noun = 0;
}
- else
- if(dictionary_flags[1] & FLAG_VERBSF)
+ else if(dictionary_flags[1] & FLAG_VERBSF)
{
// expect a verb, must have a -s suffix
tr->expect_verb = 0;
@@ -1171,8 +1611,7 @@ strcpy(phonemes2,phonemes);
tr->expect_past = 0;
tr->expect_noun = 0;
}
- else
- if(dictionary_flags[1] & FLAG_NOUNF)
+ else if(dictionary_flags[1] & FLAG_NOUNF)
{
/* not expecting a verb next */
tr->expect_noun = 2;
@@ -1196,11 +1635,11 @@ strcpy(phonemes2,phonemes);
tr->expect_past--;
}
- if((word_length == 1) && iswalpha(first_char) && (first_char != 'i'))
+ if((word_length == 1) && (tr->translator_name == L('e','n')) && iswalpha2(first_char) && (first_char != 'i'))
{
// English Specific !!!!
// any single letter before a dot is an abbreviation, except 'I'
- dictionary_flags[0] |= FLAG_DOT;
+ dictionary_flags[0] |= FLAG_ALLOW_DOT;
}
if((tr->langopts.param[LOPT_ALT] & 2) && ((dictionary_flags[0] & (FLAG_ALT_TRANS | FLAG_ALT2_TRANS)) != 0))
@@ -1208,6 +1647,8 @@ strcpy(phonemes2,phonemes);
ApplySpecialAttribute2(tr,word_phonemes,dictionary_flags[0]);
}
+ dictionary_flags[0] |= was_unpronouncable;
+ memcpy(word_start, word_copy2, word_copy_length);
return(dictionary_flags[0]);
} // end of TranslateWord
@@ -1216,8 +1657,8 @@ strcpy(phonemes2,phonemes);
static void SetPlist2(PHONEME_LIST2 *p, unsigned char phcode)
{//==========================================================
p->phcode = phcode;
- p->stress = 0;
- p->tone_number = 0;
+ p->stresslevel = 0;
+ p->tone_ph = 0;
p->synthflags = embedded_flag;
p->sourceix = 0;
embedded_flag = 0;
@@ -1236,12 +1677,63 @@ static int CountSyllables(unsigned char *phonemes)
}
+static void Word_EmbeddedCmd(void)
+{//====================
+// Process embedded commands for emphasis, sayas, and break
+ int embedded_cmd;
+ int value;
+
+ do
+ {
+ embedded_cmd = embedded_list[embedded_read++];
+ value = embedded_cmd >> 8;
+
+ switch(embedded_cmd & 0x1f)
+ {
+ case EMBED_Y:
+ option_sayas = value;
+ break;
+
+ case EMBED_F:
+ option_emphasis = value;
+ break;
+
+ case EMBED_B:
+ // break command
+ if(value == 0)
+ pre_pause = 0; // break=none
+ else
+ pre_pause += value;
+ break;
+ }
+ } while(((embedded_cmd & 0x80) == 0) && (embedded_read < embedded_ix));
+} // end of Word_EmbeddedCmd
+
+
int SetTranslator2(const char *new_language)
{//=========================================
// Set translator2 to a second language
int new_phoneme_tab;
+ const char *new_phtab_name;
+ int bitmap;
+ int dialect = 0;
- if((new_phoneme_tab = SelectPhonemeTableName(new_language)) >= 0)
+ new_phtab_name = new_language;
+ if((bitmap = translator->langopts.dict_dialect) != 0)
+ {
+ if((bitmap & (1 << DICTDIALECT_EN_US)) && (strcmp(new_language, "en") == 0))
+ {
+ new_phtab_name = "en-us";
+ dialect = DICTDIALECT_EN_US;
+ }
+ if((bitmap & (1 << DICTDIALECT_ES_LA)) && (strcmp(new_language, "es") == 0))
+ {
+ new_phtab_name = "es-la";
+ dialect = DICTDIALECT_ES_LA;
+ }
+ }
+
+ if((new_phoneme_tab = SelectPhonemeTableName(new_phtab_name)) >= 0)
{
if((translator2 != NULL) && (strcmp(new_language,translator2_language) != 0))
{
@@ -1255,14 +1747,30 @@ int SetTranslator2(const char *new_language)
translator2 = SelectTranslator(new_language);
strcpy(translator2_language,new_language);
- if(LoadDictionary(translator2, new_language, 0) != 0)
+ if(LoadDictionary(translator2, translator2->dictionary_name, 0) != 0)
{
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table
new_phoneme_tab = -1;
translator2_language[0] = 0;
}
+ else
+ {
+ if(dialect == DICTDIALECT_EN_US)
+ {
+ // en-us
+ translator2->dict_condition = 0x48; // bits 3, 6
+ translator2->langopts.param[LOPT_REDUCE_T] = 1;
+ }
+ if(dialect == DICTDIALECT_ES_LA)
+ {
+ translator2->dict_condition = 0x04; // bit 2
+ }
+ }
+ translator2->phoneme_tab_ix = new_phoneme_tab;
}
}
+ if(translator2 != NULL)
+ translator2->phonemes_repeat[0] = 0;
return(new_phoneme_tab);
} // end of SetTranslator2
@@ -1276,8 +1784,6 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
int next_tone=0;
unsigned char *p;
int srcix;
- int embedded_cmd;
- int value;
int found_dict_flag;
unsigned char ph_code;
PHONEME_LIST2 *plist2;
@@ -1293,10 +1799,23 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
int ix;
int sylimit; // max. number of syllables in a word to be combined with a preceding preposition
const char *new_language;
- unsigned char bad_phoneme[4];
+ int bad_phoneme;
int word_flags;
int word_copy_len;
char word_copy[N_WORD_BYTES+1];
+ char word_replaced[N_WORD_BYTES+1];
+ char old_dictionary_name[40];
+
+ if((f_logespeak != NULL) && (logging_type & 8))
+ {
+ fprintf(f_logespeak,"WORD: flg=%.5x len=%d '",wtab->flags,wtab->length);
+ for(ix=0; ix<40; ix++)
+ {
+ if(word[ix]==0) break;
+ fputc(word[ix], f_logespeak);
+ }
+ fprintf(f_logespeak,"'\n");
+ }
len = wtab->length;
if(len > 31) len = 31;
@@ -1305,37 +1824,26 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
word_flags = wtab[0].flags;
if(word_flags & FLAG_EMBEDDED)
{
+ wtab[0].flags &= ~FLAG_EMBEDDED; // clear it in case we call TranslateWord2() again for the same word
embedded_flag = SFLAG_EMBEDDED;
- do
- {
- embedded_cmd = embedded_list[embedded_read++];
- value = embedded_cmd >> 8;
-
- switch(embedded_cmd & 0x1f)
- {
- case EMBED_Y:
- option_sayas = value;
- break;
-
- case EMBED_F:
- option_emphasis = value;
- break;
-
- case EMBED_B:
- // break command
- if(value == 0)
- pre_pause = 0; // break=none
- else
- pre_pause += value;
- break;
- }
- } while((embedded_cmd & 0x80) == 0);
+ Word_EmbeddedCmd();
}
- if(word[0] == 0)
+ if((word[0] == 0) || (word_flags & FLAG_DELETE_WORD))
{
- // nothing to translate
+ // nothing to translate. Add a dummy phoneme to carry any embedded commands
+ if(embedded_flag)
+ {
+ ph_list2[n_ph_list2].phcode = phonEND_WORD;
+ ph_list2[n_ph_list2].stresslevel = 0;
+ ph_list2[n_ph_list2].wordstress = 0;
+ ph_list2[n_ph_list2].tone_ph = 0;
+ ph_list2[n_ph_list2].synthflags = embedded_flag;
+ ph_list2[n_ph_list2].sourceix = 0;
+ n_ph_list2++;
+ embedded_flag = 0;
+ }
word_phonemes[0] = 0;
return(0);
}
@@ -1397,7 +1905,7 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
}
else
{
- EncodePhonemes(word,word_phonemes,bad_phoneme);
+ EncodePhonemes(word,word_phonemes,&bad_phoneme);
}
flags = FLAG_FOUND;
}
@@ -1408,7 +1916,8 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
while(((c2 = word_copy[ix] = word[ix]) != ' ') && (c2 != 0) && (ix < N_WORD_BYTES)) ix++;
word_copy_len = ix;
- flags = TranslateWord(translator, word, next_pause, wtab);
+ word_replaced[2] = 0;
+ flags = TranslateWord(translator, word, next_pause, wtab, &word_replaced[2]);
if(flags & FLAG_SPELLWORD)
{
@@ -1417,86 +1926,116 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
return(flags);
}
- if((flags & FLAG_ALT2_TRANS) && ((sylimit = tr->langopts.param[LOPT_COMBINE_WORDS]) > 0))
+ if((flags & FLAG_COMBINE) && !(wtab[1].flags & FLAG_PHONEMES))
{
char *p2;
int ok = 1;
- int flags2;
+ unsigned int flags2[2];
int c_word2;
char ph_buf[N_WORD_PHONEMES];
+ flags2[0] = 0;
+ sylimit = tr->langopts.param[LOPT_COMBINE_WORDS];
+
// LANG=cs,sk
// combine a preposition with the following word
p2 = word;
while(*p2 != ' ') p2++;
utf8_in(&c_word2, p2+1); // first character of the next word;
- if(!iswalpha(c_word2))
+ if(!iswalpha2(c_word2))
{
ok =0;
}
if(ok != 0)
{
+ strcpy(ph_buf,word_phonemes);
+
+ flags2[0] = TranslateWord(translator, p2+1, 0, wtab+1, NULL);
+ if((flags2[0] & FLAG_WAS_UNPRONOUNCABLE) || (word_phonemes[0] == phonSWITCH))
+ ok = 0;
+
if(sylimit & 0x100)
{
// only if the second word has $alt attribute
- strcpy(ph_buf,word_phonemes);
- flags2 = TranslateWord(translator, p2+1, 0, wtab+1);
- if((flags2 & FLAG_ALT_TRANS) == 0)
+ if((flags2[0] & FLAG_ALT_TRANS) == 0)
{
ok = 0;
- strcpy(word_phonemes,ph_buf);
}
}
-
+
if((sylimit & 0x200) && ((wtab+1)->flags & FLAG_LAST_WORD))
{
// not if the next word is end-of-sentence
ok = 0;
}
+
+ if(ok == 0)
+ {
+ strcpy(word_phonemes,ph_buf);
+ }
}
if(ok)
{
*p2 = '-'; // replace next space by hyphen
- flags = TranslateWord(translator, word, next_pause, wtab); // translate the combined word
- if(CountSyllables(p) > (sylimit & 0xf))
+ wtab[0].flags &= ~FLAG_ALL_UPPER; // prevent it being considered an abbreviation
+ flags = TranslateWord(translator, word, next_pause, wtab, NULL); // translate the combined word
+ if((sylimit > 0) && (CountSyllables(p) > (sylimit & 0x1f)))
{
// revert to separate words
*p2 = ' ';
- flags = TranslateWord(translator, word, next_pause, wtab);
+ flags = TranslateWord(translator, word, next_pause, wtab, NULL);
}
else
{
+ if(flags == 0)
+ flags = flags2[0]; // no flags for the combined word, so use flags from the second word eg. lang-hu "nem december 7-e"
flags |= FLAG_SKIPWORDS;
dictionary_skipwords = 1;
}
}
}
- if(p[0] == phonSWITCH)
+ if(p[0]==phonSWITCH)
{
- // this word uses a different language
- memcpy(word, word_copy, word_copy_len);
+ int switch_attempt;
+ strcpy(old_dictionary_name, dictionary_name);
+ for(switch_attempt=0; switch_attempt < 2; switch_attempt++)
+ {
+ // this word uses a different language
+ memcpy(word, word_copy, word_copy_len);
- new_language = (char *)(&p[1]);
- if(new_language[0]==0)
- new_language = "en";
+ new_language = (char *)(&p[1]);
+ if(new_language[0]==0)
+ new_language = "en";
- switch_phonemes = SetTranslator2(new_language);
+ switch_phonemes = SetTranslator2(new_language);
- if(switch_phonemes >= 0)
- {
- // re-translate the word using the new translator
- flags = TranslateWord(translator2, word, next_pause, wtab);
-// strcpy((char *)p,translator2->word_phonemes);
- if(p[0] == phonSWITCH)
+ if(switch_phonemes >= 0)
{
- // the second translator doesn't want to process this word
- switch_phonemes = -1;
+ // re-translate the word using the new translator
+ wtab[0].flags |= FLAG_TRANSLATOR2;
+ if(word_replaced[2] != 0)
+ {
+ word_replaced[0] = 0; // byte before the start of the word
+ word_replaced[1] = ' ';
+ flags = TranslateWord(translator2, &word_replaced[1], next_pause, wtab, NULL);
+ }
+ else
+ flags = TranslateWord(translator2, word, next_pause, wtab, &word_replaced[2]);
}
+
+ if(p[0] != phonSWITCH)
+ break;
}
+
+ // strcpy((char *)p,translator2->word_phonemes);
+
+ if(p[0] == phonSWITCH)
+ return(FLAG_SPELLWORD);
+
if(switch_phonemes < 0)
{
// language code is not recognised or 2nd translator won't translate it
@@ -1504,6 +2043,24 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
p[1] = phonSCHWA;
p[2] = 0;
}
+
+// ?? Option to convert from language2 phonemes to the equivalent language1 phonemes
+// ?? Option to set the word-stress according to language1 rules eg. lang=fr)
+ if(ChangeEquivalentPhonemes(tr, switch_phonemes, (char *)p))
+ {
+ // Phonemes have been converted from the foreign language to the native language
+ switch_phonemes = -1;
+ }
+
+ if(switch_phonemes == -1)
+ {
+ strcpy(dictionary_name, old_dictionary_name);
+ SelectPhonemeTable(voice->phoneme_tab_ix);
+
+ // leave switch_phonemes set, but use the original phoneme table number.
+ // This will suppress LOPT_REGRESSIVE_VOICING
+ switch_phonemes = voice->phoneme_tab_ix; // original phoneme table
+ }
}
if(!(word_flags & FLAG_HYPHEN))
@@ -1513,7 +2070,7 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
if(pre_pause < 1)
pre_pause = 1;
}
- if((flags & FLAG_PREPAUSE) && ((word_flags && FLAG_LAST_WORD) == 0) && (tr->prepause_timeout == 0))
+ if((flags & FLAG_PREPAUSE) && !(word_flags && (FLAG_LAST_WORD | FLAG_FIRST_WORD)) && !(wtab[-1].flags & FLAG_FIRST_WORD) && (tr->prepause_timeout == 0))
{
// the word is marked in the dictionary list with $pause
if(pre_pause < 4) pre_pause = 4;
@@ -1525,14 +2082,13 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
pre_pause = 1;
}
- plist2 = &ph_list2[n_ph_list2];
stress = 0;
- next_stress = 0;
+ next_stress = 1;
srcix = 0;
max_stress = -1;
found_dict_flag = 0;
- if(flags & FLAG_FOUND)
+ if((flags & FLAG_FOUND) && !(flags & FLAG_TEXTMODE))
found_dict_flag = SFLAG_DICTIONARY;
while((pre_pause > 0) && (n_ph_list2 < N_PHONEME_LIST-4))
@@ -1550,8 +2106,10 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
pre_pause--;
}
tr->end_stressed_vowel = 0; // forget about the previous word
- tr->prev_dict_flags = 0;
+ tr->prev_dict_flags[0] = 0;
+ tr->prev_dict_flags[1] = 0;
}
+ plist2 = &ph_list2[n_ph_list2];
if((option_capitals==1) && (word_flags & FLAG_FIRST_UPPER))
{
@@ -1567,15 +2125,42 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
if(switch_phonemes >= 0)
{
- // this word uses a different phoneme table
- SetPlist2(&ph_list2[n_ph_list2],phonSWITCH);
- ph_list2[n_ph_list2++].tone_number = switch_phonemes; // temporary phoneme table number
+ if((p[0] == phonPAUSE) && (p[1] == phonSWITCH))
+ {
+ // the new word starts with a phoneme table switch, so there's no need to switch before it.
+ if(ph_list2[n_ph_list2-1].phcode == phonSWITCH)
+ {
+ //previous phoneme is also a phonSWITCH, delete it
+ n_ph_list2--;
+ }
+ }
+ else
+ {
+ // this word uses a different phoneme table
+ if(ph_list2[n_ph_list2-1].phcode == phonSWITCH)
+ {
+ //previous phoneme is also a phonSWITCH, just change its phoneme table number
+ n_ph_list2--;
+ }
+ else
+ {
+ SetPlist2(&ph_list2[n_ph_list2],phonSWITCH);
+ }
+ ph_list2[n_ph_list2++].tone_ph = switch_phonemes; // temporary phoneme table number
+ }
}
// remove initial pause from a word if it follows a hyphen
if((word_flags & FLAG_HYPHEN) && (phoneme_tab[*p]->type == phPAUSE))
p++;
+ if((p[0] == 0) && (embedded_flag))
+ {
+ // no phonemes. Insert a very short pause to carry an embedded command
+ p[0] = phonPAUSE_VSHORT;
+ p[1] = 0;
+ }
+
while(((ph_code = *p++) != 0) && (n_ph_list2 < N_PHONEME_LIST-4))
{
if(ph_code == 255)
@@ -1588,23 +2173,24 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
{
ph_list2[n_ph_list2].phcode = ph_code;
ph_list2[n_ph_list2].sourceix = 0;
- ph_list2[n_ph_list2].synthflags = embedded_flag;
- ph_list2[n_ph_list2++].tone_number = *p++;
+ ph_list2[n_ph_list2].synthflags = 0;
+ ph_list2[n_ph_list2++].tone_ph = *p;
+ SelectPhonemeTable(*p);
+ p++;
}
- else
- if(ph->type == phSTRESS)
+ else if(ph->type == phSTRESS)
{
// don't add stress phonemes codes to the list, but give their stress
// value to the next vowel phoneme
// std_length is used to hold stress number or (if >10) a tone number for a tone language
- if(ph->spect == 0)
+ if(ph->program == 0)
next_stress = ph->std_length;
else
{
// for tone languages, the tone number for a syllable follows the vowel
if(prev_vowel >= 0)
{
- ph_list2[prev_vowel].tone_number = ph_code;
+ ph_list2[prev_vowel].tone_ph = ph_code;
}
else
{
@@ -1612,31 +2198,27 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
}
}
}
- else
- if(ph_code == phonSYLLABIC)
+ else if(ph_code == phonSYLLABIC)
{
// mark the previous phoneme as a syllabic consonant
prev_vowel = n_ph_list2-1;
ph_list2[prev_vowel].synthflags |= SFLAG_SYLLABLE;
- ph_list2[prev_vowel].stress = next_stress;
+ ph_list2[prev_vowel].stresslevel = next_stress;
}
- else
- if(ph_code == phonLENGTHEN)
+ else if(ph_code == phonLENGTHEN)
{
ph_list2[n_ph_list2-1].synthflags |= SFLAG_LENGTHEN;
}
- else
- if(ph_code == phonEND_WORD)
+ else if(ph_code == phonEND_WORD)
{
// a || symbol in a phoneme string was used to indicate a word boundary
// Don't add this phoneme to the list, but make sure the next phoneme has
// a newword indication
srcix = source_ix+1;
}
- else
- if(ph_code == phonX1)
+ else if(ph_code == phonX1)
{
- // a language specific action
+ // a language specific action
if(tr->langopts.param[LOPT_IT_DOUBLING])
{
flags |= FLAG_DOUBLING;
@@ -1645,7 +2227,7 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
else
{
ph_list2[n_ph_list2].phcode = ph_code;
- ph_list2[n_ph_list2].tone_number = 0;
+ ph_list2[n_ph_list2].tone_ph = 0;
ph_list2[n_ph_list2].synthflags = embedded_flag | found_dict_flag;
embedded_flag = 0;
ph_list2[n_ph_list2].sourceix = srcix;
@@ -1654,10 +2236,15 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
if(ph->type == phVOWEL)
{
stress = next_stress;
- next_stress = 0;
+ next_stress = 1; // default is 'unstressed'
+
+ if(stress >= 4)
+ {
+ any_stressed_words = 1;
+ }
if((prev_vowel >= 0) && (n_ph_list2-1) != prev_vowel)
- ph_list2[n_ph_list2-1].stress = stress; // set stress for previous consonant
+ ph_list2[n_ph_list2-1].stresslevel = stress; // set stress for previous consonant
ph_list2[n_ph_list2].synthflags |= SFLAG_SYLLABLE;
prev_vowel = n_ph_list2;
@@ -1669,7 +2256,7 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
}
if(next_tone != 0)
{
- ph_list2[n_ph_list2].tone_number = next_tone;
+ ph_list2[n_ph_list2].tone_ph = next_tone;
next_tone=0;
}
}
@@ -1677,8 +2264,8 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
{
if(first_phoneme && tr->langopts.param[LOPT_IT_DOUBLING])
{
- if(((tr->prev_dict_flags & FLAG_DOUBLING) && (tr->langopts.param[LOPT_IT_DOUBLING] & 1)) ||
- (tr->end_stressed_vowel && (tr->langopts.param[LOPT_IT_DOUBLING] & 2)))
+ if(((tr->prev_dict_flags[0] & FLAG_DOUBLING) && (tr->langopts.param[LOPT_IT_DOUBLING] & 1)) ||
+ (tr->end_stressed_vowel && (tr->langopts.param[LOPT_IT_DOUBLING] & 2)))
{
// italian, double the initial consonant if the previous word ends with a
// stressed vowel, or is marked with a flag
@@ -1687,11 +2274,17 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
}
}
- ph_list2[n_ph_list2].stress = stress;
+ ph_list2[n_ph_list2].stresslevel = stress;
n_ph_list2++;
first_phoneme = 0;
}
}
+
+ if(word_flags & FLAG_COMMA_AFTER)
+ {
+ SetPlist2(&ph_list2[n_ph_list2++],phonPAUSE_CLAUSE);
+ }
+
// don't set new-word if there is a hyphen before it
if((word_flags & FLAG_HYPHEN) == 0)
{
@@ -1707,9 +2300,10 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
if(switch_phonemes >= 0)
{
// this word uses a different phoneme table, now switch back
+ strcpy(dictionary_name, old_dictionary_name);
SelectPhonemeTable(voice->phoneme_tab_ix);
SetPlist2(&ph_list2[n_ph_list2],phonSWITCH);
- ph_list2[n_ph_list2++].tone_number = voice->phoneme_tab_ix; // original phoneme table number
+ ph_list2[n_ph_list2++].tone_ph = voice->phoneme_tab_ix; // original phoneme table number
}
@@ -1726,14 +2320,14 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa
ph_list2[max_stress_ix].synthflags |= SFLAG_PROMOTE_STRESS;
}
- tr->prev_dict_flags = flags;
+ tr->prev_dict_flags[0] = flags;
return(flags);
} // end of TranslateWord2
-static int EmbeddedCommand(unsigned int *source_index)
-{//===================================================
+static int EmbeddedCommand(unsigned int *source_index_out)
+{//=======================================================
// An embedded command to change the pitch, volume, etc.
// returns number of commands added to embedded_list
@@ -1744,28 +2338,28 @@ static int EmbeddedCommand(unsigned int *source_index)
unsigned char c;
char *p;
int cmd;
+ int source_index = *source_index_out;
- c = source[*source_index];
+ c = source[source_index];
if(c == '+')
{
sign = 0x40;
- (*source_index)++;
+ source_index++;
}
- else
- if(c == '-')
+ else if(c == '-')
{
sign = 0x60;
- (*source_index)++;
+ source_index++;
}
- if(isdigit(source[*source_index]))
+ if(IsDigit09(source[source_index]))
{
- value = atoi(&source[*source_index]);
- while(isdigit(source[*source_index]))
+ value = atoi(&source[source_index]);
+ while(IsDigit09(source[source_index]))
source_index++;
}
- c = source[(*source_index)++];
+ c = source[source_index++];
if(embedded_ix >= (N_EMBEDDED_LIST - 2))
return(0); // list is full
@@ -1792,12 +2386,13 @@ static int EmbeddedCommand(unsigned int *source_index)
}
embedded_list[embedded_ix++] = cmd + sign + (value << 8);
+ *source_index_out = source_index;
return(1);
} // end of EmbeddedCommand
-static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in, int *insert)
+static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in, int *insert, int *wordflags)
{//=========================================================================================
int ix;
unsigned int word;
@@ -1818,9 +2413,9 @@ static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in,
// there is a list of character codes to be substituted with alternative codes
- if(iswupper(c_lower = c))
+ if(iswupper2(c_lower = c))
{
- c_lower = towlower(c);
+ c_lower = towlower2(c);
upper_case = 1;
}
@@ -1834,7 +2429,7 @@ static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in,
new_c = replace_chars[ix+1];
break;
}
- if((word >> 16) == (unsigned int)towlower(next_in))
+ if((word >> 16) == (unsigned int)towlower2(next_in))
{
new_c = replace_chars[ix+1];
ignore_next = 1;
@@ -1851,20 +2446,22 @@ static int SubstituteChar(Translator *tr, unsigned int c, unsigned int next_in,
// there is a second character to be inserted
// don't convert the case of the second character unless the next letter is also upper case
c2 = new_c >> 16;
- if(upper_case && iswupper(next_in))
- c2 = towupper(c2);
+ if(upper_case && iswupper2(next_in))
+ c2 = towupper2(c2);
*insert = c2;
new_c &= 0xffff;
}
if(upper_case)
- new_c = towupper(new_c);
+ new_c = towupper2(new_c);
+
+ *wordflags |= FLAG_CHAR_REPLACED;
return(new_c);
}
-static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c, unsigned int next_in, int *insert)
+static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c, unsigned int next_in, int *insert, int *wordflags)
{//================================================================================================================
// To allow language specific examination and replacement of characters
@@ -1875,64 +2472,101 @@ static int TranslateChar(Translator *tr, char *ptr, int prev_in, unsigned int c,
int next2;
static const unsigned char hangul_compatibility[0x34] = {
- 0, 0x00,0x01,0xaa,0x02,0xac,0xad,0x03,
- 0x04,0x05,0xb0,0xb1,0xb2,0xb3,0xb4,0xb4,
- 0xb6,0x06,0x07,0x08,0xb9,0x09,0x0a,0xbc,
- 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x61,
- 0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
- 0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,
- 0x72,0x73,0x74,0x75 };
+ 0, 0x00,0x01,0xaa,0x02,0xac,0xad,0x03,
+ 0x04,0x05,0xb0,0xb1,0xb2,0xb3,0xb4,0xb4,
+ 0xb6,0x06,0x07,0x08,0xb9,0x09,0x0a,0xbc,
+ 0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x61,
+ 0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
+ 0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,
+ 0x72,0x73,0x74,0x75
+ };
+
+ // check for Korean Hangul letters
+ if(((code = c - 0xac00) >= 0) && (c <= 0xd7af))
+ {
+ // break a syllable hangul into 2 or 3 individual jamo
+ initial = (code/28)/21;
+ medial = (code/28) % 21;
+ final = code % 28;
+
+ if(initial == 11)
+ {
+ // null initial
+ c = medial + 0x1161;
+ if(final > 0)
+ *insert = final + 0x11a7;
+ }
+ else
+ {
+ // extact the initial and insert the remainder with a null initial
+ c = initial + 0x1100;
+ *insert = (11*28*21) + (medial*28) + final + 0xac00;
+ }
+ return(c);
+ }
+ else if(((code = c - 0x3130) >= 0) && (code < 0x34))
+ {
+ // Hangul compatibility jamo
+ return(hangul_compatibility[code] + 0x1100);
+ }
switch(tr->translator_name)
{
case L('a','f'):
- // look for 'n and replace by a special character (unicode: schwa)
+ case L('n','l'):
+ // look for 'n and replace by a special character (unicode: schwa)
- utf8_in(&next2, &ptr[1]);
- if(!iswalpha(prev_in))
+ if(!iswalpha2(prev_in))
{
- if((c == '\'') && (next_in == 'n') && IsSpace(next2))
+ utf8_in(&next2, &ptr[1]);
+
+ if((c == '\'') && IsSpace(next2))
{
- // n preceded by either apostrophe or U2019 "right single quotation mark"
- ptr[0] = ' '; // delete the n
- return(0x0259); // replace ' by unicode schwa character
+ if((next_in == 'n') && (tr->translator_name == L('a','f')))
+ {
+ // n preceded by either apostrophe or U2019 "right single quotation mark"
+ ptr[0] = ' '; // delete the n
+ return(0x0259); // replace ' by unicode schwa character
+ }
+ if((next_in == 'n') || (next_in == 't'))
+ {
+ // Dutch, [@n] and [@t]
+ return(0x0259); // replace ' by unicode schwa character
+ }
}
}
break;
+ }
+ return(SubstituteChar(tr, c, next_in, insert, wordflags));
+}
- case L('k','o'):
- if(((code = c - 0xac00) >= 0) && (c <= 0xd7af))
+
+static const char *UCase_ga[] = {"bp","bhf","dt","gc","hA","mb","nd","ng","ts","tA","nA",NULL};
+
+static int UpperCaseInWord(Translator *tr, char *word, int c)
+{//=====================================================
+ int ix;
+ int len;
+ const char *p;
+
+ if(tr->translator_name == L('g','a'))
+ {
+ // Irish
+ for(ix=0; ; ix++)
{
- // break a syllable hangul into 2 or 3 individual jamo
- initial = (code/28)/21;
- medial = (code/28) % 21;
- final = code % 28;
+ if((p = UCase_ga[ix]) == NULL)
+ break;
- if(initial == 11)
- {
- // null initial
- c = medial + 0x1161;
- if(final > 0)
- *insert = final + 0x11a7;
- }
- else
+ len = strlen(p);
+ if((word[-len]==' ') && (memcmp(&word[-len+1], p, len-1) == 0))
{
- // extact the initial and insert the remainder with a null initial
- c = initial + 0x1100;
- *insert = (11*28*21) + (medial*28) + final + 0xac00;
+ if((c == p[len-1]) || ((p[len-1]=='A') && IsVowel(tr, c)))
+ return(1);
}
- return(c);
}
- else
- if(((code = c - 0x3130) >= 0) && (code < 0x34))
- {
- // Hangul compatibility jamo
- return(hangul_compatibility[code] + 0x1100);
- }
- break;
}
- return(SubstituteChar(tr,c,next_in,insert));
+ return(0);
}
@@ -1943,11 +2577,13 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
int cc;
unsigned int source_index=0;
unsigned int prev_source_index=0;
+ int source_index_word=0;
int prev_in;
int prev_out=' ';
int prev_out2;
- int prev_in2=0;
+ int prev_in_save=0;
int next_in;
+ int next_in_nbytes;
int char_inserted=0;
int clause_pause;
int pre_pause_add=0;
@@ -1959,6 +2595,7 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
int dict_flags = 0; // returned from dictionary lookup
int word_flags; // set here
int next_word_flags;
+ int new_sentence2;
int embedded_count = 0;
int letter_count = 0;
int space_inserted = 0;
@@ -1972,6 +2609,7 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
short charix[N_TR_SOURCE+4];
WORD_TAB words[N_CLAUSE_WORDS];
+ static char voice_change_name[40];
int word_count=0; // index into words
char sbuf[N_TR_SOURCE];
@@ -1980,13 +2618,18 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
int tone;
int tone2;
+ if(tr==NULL)
+ {
+ return(NULL);
+ }
+
p_textinput = (unsigned char *)vp_input;
p_wchar_input = (wchar_t *)vp_input;
embedded_ix = 0;
embedded_read = 0;
- option_phoneme_input &= ~2; // clear bit 1 (temporary indication)
pre_pause = 0;
+ any_stressed_words = 0;
if((clause_start_char = count_characters) < 0)
clause_start_char = 0;
@@ -1994,14 +2637,27 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
for(ix=0; ix<N_TR_SOURCE; ix++)
charix[ix] = 0;
- terminator = ReadClause(tr, f_text, source, charix, &charix_top, N_TR_SOURCE, &tone2);
+ terminator = ReadClause(tr, f_text, source, charix, &charix_top, N_TR_SOURCE, &tone2, voice_change_name);
+
+ if((f_logespeak != NULL) && (logging_type & 4))
+ {
+ fprintf(f_logespeak,"CLAUSE %x:\n",terminator);
+ for(p=source; *p != 0; p++)
+ fputc(*p, f_logespeak);
+ fprintf(f_logespeak,"ENDCLAUSE\n");
+ fflush(f_logespeak);
+ }
+ p = source;
charix[charix_top+1] = 0;
charix[charix_top+2] = 0x7fff;
charix[charix_top+3] = 0;
clause_pause = (terminator & 0xfff) * 10; // mS
- tone = (terminator >> 12) & 0xf;
+ if(terminator & CLAUSE_PAUSE_LONG)
+ clause_pause = clause_pause * 32 ; // pause value is *320mS not *10mS
+
+ tone = (terminator >> 12) & 0x7;
if(tone2 != 0)
{
// override the tone type
@@ -2023,15 +2679,19 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
if(clause_pause < 0)
clause_pause = 0;
- terminator &= ~CLAUSE_BIT_SENTENCE; // clear sentence bit
+ if(new_sentence)
+ terminator |= CLAUSE_BIT_SENTENCE; // carry forward an end-of-sentence indicator
max_clause_pause += clause_pause;
+ new_sentence2 = 0;
}
else
{
max_clause_pause = clause_pause;
+ new_sentence2 = new_sentence;
}
+ tr->clause_terminator = terminator;
- if(new_sentence)
+ if(new_sentence2)
{
count_sentences++;
if(skip_sentences > 0)
@@ -2054,7 +2714,8 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
tr->expect_verb_s=0;
tr->phonemes_repeat_count = 0;
tr->end_stressed_vowel=0;
- tr->prev_dict_flags = 0;
+ tr->prev_dict_flags[0] = 0;
+ tr->prev_dict_flags[1] = 0;
word_count = 0;
single_quoted = 0;
@@ -2071,7 +2732,7 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
words[0].flags = 0;
finished = 0;
- for(j=0; charix[j]==0; j++);
+ for(j=0; charix[j]<=0; j++);
words[0].sourceix = charix[j];
k = 0;
while(charix[j] != 0)
@@ -2093,13 +2754,12 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
prev_out = 'a';
}
- if(prev_in2 != 0)
+ if(prev_in_save != 0)
{
- prev_in = prev_in2;
- prev_in2 = 0;
+ prev_in = prev_in_save;
+ prev_in_save = 0;
}
- else
- if(source_index > 0)
+ else if(source_index > 0)
{
utf8_in2(&prev_in,&source[source_index-1],1); // prev_in = source[source_index-1];
}
@@ -2116,7 +2776,13 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
source_index += utf8_in(&cc,&source[source_index]); // cc = source[source_index++];
c = cc;
}
- utf8_in(&next_in,&source[source_index]);
+ next_in_nbytes = utf8_in(&next_in,&source[source_index]);
+
+ if(c == 0)
+ {
+ finished = 1;
+ c = ' ';
+ }
if((c == CTRL_EMBEDDED) || (c == ctrl_embedded))
{
@@ -2126,13 +2792,13 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
if(prev_in != ' ')
{
c = ' ';
- prev_in2 = c;
+ prev_in_save = c;
source_index--;
}
else
{
embedded_count += EmbeddedCommand(&source_index);
- prev_in2 = prev_in;
+ prev_in_save = prev_in;
// replace the embedded command by spaces
memset(&source[srcix],' ',source_index-srcix);
source_index = srcix;
@@ -2140,15 +2806,14 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
}
}
- if(option_sayas2 == SAYAS_KEY)
+ if((option_sayas2 == SAYAS_KEY) && (c != ' '))
{
- if(((c == '_') || (c == '-')) && IsAlpha(prev_in))
- {
- c = ' ';
- }
+ if((prev_in == ' ') && (next_in == ' '))
+ option_sayas2 = SAYAS_SINGLE_CHARS; // single character, speak its name
c = towlower2(c);
}
+
if(phoneme_mode)
{
all_upper_case = FLAG_PHONEMES;
@@ -2160,8 +2825,7 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
c = ' ';
}
}
- else
- if((option_sayas2 & 0xf0) == SAYAS_DIGITS)
+ else if((option_sayas2 & 0xf0) == SAYAS_DIGITS)
{
if(iswdigit(c))
{
@@ -2184,8 +2848,7 @@ void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *t
}
}
}
- else
- if((option_sayas2 & 0x30) == 0)
+ else if((option_sayas2 & 0x10) == 0)
{
// speak as words
@@ -2199,9 +2862,9 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
}
#endif
if((c == 0x92) || (c == 0xb4) || (c == 0x2019) || (c == 0x2032))
- c = '\''; // 'microsoft' quote or sexed closing single quote, or prime - possibly used as apostrophe
+ c = '\''; // 'microsoft' quote or sexed closing single quote, or prime - possibly used as apostrophe
- if((c == '?') && IsAlpha(prev_out) && IsAlpha(next_in))
+ if(((c == 0x2018) || (c == '?')) && IsAlpha(prev_out) && IsAlpha(next_in))
{
// ? between two letters may be a smart-quote replaced by ?
c = '\'';
@@ -2214,7 +2877,13 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
word_flags |= FLAG_FOCUS;
}
- c = TranslateChar(tr, &source[source_index], prev_in,c, next_in, &char_inserted); // optional language specific function
+ if(c == CHAR_COMMA_BREAK)
+ {
+ c = ' ';
+ word_flags |= FLAG_COMMA_AFTER;
+ }
+
+ c = TranslateChar(tr, &source[source_index], prev_in,c, next_in, &char_inserted, &word_flags); // optional language specific function
if(c == 8)
continue; // ignore this character
@@ -2240,7 +2909,7 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
if(iswdigit(prev_out))
{
- if(!iswdigit(c) && (c != '.') && (c != ','))
+ if(!iswdigit(c) && (c != '.') && (c != ',') && (c != ' '))
{
c = ' '; // terminate digit string with a space
space_inserted = 1;
@@ -2254,19 +2923,17 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
}
}
- if((c == '[') && (next_in == '[') && option_phoneme_input)
+ if(c == '[')
{
- phoneme_mode = FLAG_PHONEMES;
- source_index++;
- continue;
+ if((next_in == '\002') || ((next_in == '[') && option_phoneme_input))
+ {
+ // "[\002" is used internally to start phoneme mode
+ phoneme_mode = FLAG_PHONEMES;
+ source_index++;
+ continue;
+ }
}
- if(c == 0)
- {
- finished = 1;
- c = ' ';
- }
- else
if(IsAlpha(c))
{
if(!IsAlpha(prev_out) || (tr->langopts.ideographs && ((c > 0x3040) || (prev_out > 0x3040))))
@@ -2279,10 +2946,15 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
// start of word, insert space if not one there already
c = ' ';
space_inserted = 1;
+
+ if(!IsBracket(prev_out)) // ?? perhaps only set FLAG_NOSPACE for . - / (hyphenated words, URLs, etc)
+ {
+ next_word_flags |= FLAG_NOSPACE;
+ }
}
else
{
- if(iswupper(c))
+ if(iswupper2(c))
word_flags |= FLAG_FIRST_UPPER;
if((prev_out == ' ') && iswdigit(sbuf[ix-2]) && !iswdigit(prev_in))
@@ -2295,9 +2967,29 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
}
}
- letter_count++;
+ if(c != ' ')
+ {
+ letter_count++;
+
+ if(tr->letter_bits_offset > 0)
+ {
+ if(((c < 0x250) && (prev_out >= tr->letter_bits_offset)) ||
+ ((c >= tr->letter_bits_offset) && (letter_count > 1) && (prev_out < 0x250)))
+ {
+ // Don't mix native and Latin characters in the same word
+ // Break into separate words
+ if(IsAlpha(prev_out))
+ {
+ c = ' ';
+ space_inserted = 1;
+ word_flags |= FLAG_HYPHEN_AFTER;
+ next_word_flags |= FLAG_HYPHEN;
+ }
+ }
+ }
+ }
- if(iswupper(c))
+ if(iswupper2(c))
{
c = towlower2(c);
@@ -2312,20 +3004,40 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
}
else
{
- if(iswlower(prev_in))
+ if(iswlower2(prev_in))
{
- c = ' '; // lower case followed by upper case, treat as new word
- space_inserted = 1;
- prev_in2 = c;
+ // lower case followed by upper case in a word
+ if(UpperCaseInWord(tr, &sbuf[ix], c) == 1)
+ {
+ // convert to lower case and continue
+ c = towlower2(c);
+ }
+ else
+ {
+ c = ' '; // lower case followed by upper case, treat as new word
+ space_inserted = 1;
+ prev_in_save = c;
+ // next_word_flags |= FLAG_NOSPACE; // problem: prevents FLAG_HAS_DOT being set
+ }
}
- else
- if((c != ' ') && iswupper(prev_in) && iswlower(next_in) &&
- (memcmp(&source[source_index],"s ",2) != 0)) // ENGLISH specific plural
+ else if((c != ' ') && iswupper2(prev_in) && iswlower2(next_in))
{
- c = ' '; // change from upper to lower case, start new word at the last uppercase
- space_inserted = 1;
- prev_in2 = c;
- next_word_flags |= FLAG_NOSPACE;
+ int next2_in;
+ utf8_in(&next2_in,&source[source_index + next_in_nbytes]);
+
+ if((tr->translator_name == L('n','l')) && (letter_count==2) && (c == 'j') && (prev_in == 'I'))
+ {
+ // Dutch words may capitalise initial IJ, don't split
+ }
+ else
+ if(IsAlpha(next2_in))
+ {
+ // changing from upper to lower case, start new word at the last uppercase, if 3 or more letters
+ c = ' ';
+ space_inserted = 1;
+ prev_in_save = c;
+ next_word_flags |= FLAG_NOSPACE;
+ }
}
}
}
@@ -2348,49 +3060,78 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
all_upper_case = 0;
}
}
- else
- if(c=='-')
+ else if(c=='-')
{
- if(IsAlpha(prev_in) && IsAlpha(next_in))
+ if(!IsSpace(prev_in) && IsAlpha(next_in))
{
- // '-' between two letters is a hyphen, treat as a space
- word_flags |= FLAG_HYPHEN;
- words[word_count-1].flags |= FLAG_HYPHEN_AFTER;
- c = ' ';
+ if(prev_out != ' ')
+ {
+ // previous 'word' not yet ended (not alpha or numeric), start new word now.
+ c = ' ';
+ space_inserted = 1;
+ }
+ else
+ {
+ // '-' between two letters is a hyphen, treat as a space
+ word_flags |= FLAG_HYPHEN;
+ if(word_count > 0)
+ words[word_count-1].flags |= FLAG_HYPHEN_AFTER;
+ c = ' ';
+ }
}
- else
- if((prev_in==' ') && (next_in==' '))
+ else if((prev_in==' ') && (next_in==' '))
{
// ' - ' dash between two spaces, treat as pause
c = ' ';
pre_pause_add = 4;
}
- else
- if(next_in=='-')
+ else if(next_in=='-')
{
// double hyphen, treat as pause
source_index++;
c = ' ';
pre_pause_add = 4;
}
- else
- if((prev_out == ' ') && IsAlpha(sbuf[ix-2]) && !IsAlpha(prev_in))
+ else if((prev_out == ' ') && IsAlpha(prev_out2) && !IsAlpha(prev_in))
{
// insert extra space between a word + space + hyphen, to distinguish 'a -2' from 'a-2'
sbuf[ix++] = ' ';
words[word_count].start++;
}
}
- else
- if(c == '\'')
+ else if(c == '.')
{
- if(iswalnum(prev_in) && IsAlpha(next_in))
+ if(prev_out == '.')
+ {
+ // multiple dots, separate by spaces. Note >3 dots has been replaced by elipsis
+ c = ' ';
+ space_inserted = 1;
+ }
+ else if((word_count > 0) && !(words[word_count-1].flags & FLAG_NOSPACE) && IsAlpha(prev_in))
{
- // between two letters, consider apostrophe as part of the word
+ // dot after a word, with space following, probably an abbreviation
+ words[word_count-1].flags |= FLAG_HAS_DOT;
+
+ if(IsSpace(next_in) || (next_in == '-'))
+ c = ' '; // remove the dot if it's followed by a space or hyphen, so that it's not pronounced
+ }
+ }
+ else if(c == '\'')
+ {
+ if(((prev_in == '.') || iswalnum(prev_in)) && IsAlpha(next_in))
+ {
+ // between two letters, or in an abbreviation (eg. u.s.a.'s). Consider the apostrophe as part of the word
single_quoted = 0;
}
- else
- if((wcschr(tr->char_plus_apostrophe,prev_in) != 0) && (prev_out2 == ' '))
+ else if((tr->langopts.param[LOPT_APOSTROPHE] & 1) && IsAlpha(next_in))
+ {
+ single_quoted = 0; // apostrophe at start of word is part of the word
+ }
+ else if((tr->langopts.param[LOPT_APOSTROPHE] & 2) && IsAlpha(prev_in))
+ {
+ single_quoted = 0; // apostrophe at end of word is part of the word
+ }
+ else if((wcschr(tr->char_plus_apostrophe,prev_in) != 0) && (prev_out2 == ' '))
{
// consider single character plus apostrophe as a word
single_quoted = 0;
@@ -2421,62 +3162,76 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
else
#ifdef deleted
// Brackets are now recognised in TranslateRules()
- if(IsBracket(c))
- {
- pre_pause_add = 4;
- c = ' ';
- }
- else
-#endif
- if(lookupwchar(breaks,c) != 0)
- {
- c = ' '; // various characters to treat as space
- }
- else
- if(iswdigit(c))
- {
- if(tr->langopts.tone_numbers && IsAlpha(prev_out) && !IsDigit(next_in))
+ if(IsBracket(c))
{
+ pre_pause_add = 4;
+ c = ' ';
}
else
- if((prev_out != ' ') && !iswdigit(prev_out))
- {
- if((prev_out != tr->langopts.decimal_sep) || ((decimal_sep_count > 0) && (tr->langopts.decimal_sep == ',')))
+#endif
+ if(lookupwchar(breaks,c) != 0)
{
- c = ' ';
- space_inserted = 1;
+ c = ' '; // various characters to treat as space
}
- else
+ else if(iswdigit(c))
{
- decimal_sep_count = 1;
+ if(tr->langopts.tone_numbers && IsAlpha(prev_out) && !IsDigit(next_in))
+ {
+ }
+ else if((prev_out != ' ') && !iswdigit(prev_out))
+ {
+ if((prev_out != tr->langopts.decimal_sep) || ((decimal_sep_count > 0) && (tr->langopts.decimal_sep == ',')))
+ {
+ c = ' ';
+ space_inserted = 1;
+ }
+ else
+ {
+ decimal_sep_count = 1;
+ }
+ }
+ else if((prev_out == ' ') && IsAlpha(prev_out2) && !IsAlpha(prev_in))
+ {
+ // insert extra space between a word and a number, to distinguish 'a 2' from 'a2'
+ sbuf[ix++] = ' ';
+ words[word_count].start++;
+ }
}
- }
- else
- if((prev_out == ' ') && IsAlpha(sbuf[ix-2]) && !IsAlpha(prev_in))
- {
- // insert extra space between a word and a number, to distinguish 'a 2' from 'a2'
- sbuf[ix++] = ' ';
- words[word_count].start++;
- }
- }
}
if(IsSpace(c))
{
if(prev_out == ' ')
{
+ word_flags |= FLAG_MULTIPLE_SPACES;
continue; // multiple spaces
}
+ if((cc == 0x09) || (cc == 0x0a))
+ {
+ next_word_flags |= FLAG_MULTIPLE_SPACES; // tab or newline, not a simple space
+ }
+
if(space_inserted)
{
- words[word_count].length = source_index - words[word_count].sourceix;
+ // count the number of characters since the start of the word
+ j = 0;
+ k = source_index - 1;
+ while((k >= source_index_word) && (charix[k] != 0))
+ {
+ if(charix[k] > 0) // don't count initial bytes of multi-byte character
+ j++;
+ k--;
+ }
+ words[word_count].length = j;
}
+ source_index_word = source_index;
+
// end of 'word'
sbuf[ix++] = ' ';
- if((ix > words[word_count].start) && (word_count < N_CLAUSE_WORDS-1))
+ if((word_count < N_CLAUSE_WORDS-1) && (ix > words[word_count].start))
{
if(embedded_count > 0)
{
@@ -2533,7 +3288,8 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
}
else
{
- ix += utf8_out(c,&sbuf[ix]); // sbuf[ix++] = c;
+ if((ix < (N_TR_SOURCE - 4)))
+ ix += utf8_out(c,&sbuf[ix]); // sbuf[ix++] = c;
}
if(pre_pause_add > pre_pause)
pre_pause = pre_pause_add;
@@ -2543,7 +3299,7 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
if((word_count==0) && (embedded_count > 0))
{
// add a null 'word' to carry the embedded command flag
- embedded_list[embedded_ix-1] |= 0x80;
+ embedded_list[embedded_ix-1] |= 0x80;
words[word_count].flags |= FLAG_EMBEDDED;
word_count = 1;
}
@@ -2553,20 +3309,28 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
words[0].pre_pause = 0; // don't add extra pause at beginning of clause
words[word_count].pre_pause = 8;
if(word_count > 0)
- words[word_count-1].flags |= FLAG_LAST_WORD;
+ {
+ ix = word_count-1;
+ while((ix > 0) && (IsBracket(sbuf[words[ix].start])))
+ ix--; // the last word is a bracket, mark the previous word as last
+ words[ix].flags |= FLAG_LAST_WORD;
+
+ // FLAG_NOSPACE check to avoid recognizing .mr -mr
+ if((terminator & CLAUSE_DOT) && !(words[word_count-1].flags & FLAG_NOSPACE))
+ words[word_count-1].flags |= FLAG_HAS_DOT;
+ }
words[0].flags |= FLAG_FIRST_WORD;
- for(ix=0; ix<word_count; ix++)
+
+ for(ix=0; ix < word_count; ix++)
{
int nx;
int c_temp;
char *pn;
char *pw;
- static unsigned int break_numbers1 = 0x49249248;
- static unsigned int break_numbers2 = 0x24924aa8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi
- static unsigned int break_numbers3 = 0x49249268; // for languages which have numbers for 100,000 and 1,000,000
- unsigned int break_numbers;
- char number_buf[80];
+ int nw;
+ char number_buf[150];
+ WORD_TAB num_wtab[50]; // copy of 'words', when splitting numbers into parts
// start speaking at a specified word position in the text?
count_words++;
@@ -2579,22 +3343,23 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
if(skipping_text)
continue;
+ current_alphabet = NULL;
// digits should have been converted to Latin alphabet ('0' to '9')
word = pw = &sbuf[words[ix].start];
- if(iswdigit(word[0]) && (tr->langopts.numbers2 & NUM2_100000))
+ if(iswdigit(word[0]) && (tr->langopts.break_numbers != BREAK_THOUSANDS))
{
// Languages with 100000 numbers. Remove thousands separators so that we can insert them again later
pn = number_buf;
- while(pn < &number_buf[sizeof(number_buf)-3])
+ while(pn < &number_buf[sizeof(number_buf)-20])
{
if(iswdigit(*pw))
{
*pn++ = *pw++;
}
- else
- if((*pw == tr->langopts.thousands_sep) && (pw[1] == ' ') && iswdigit(pw[2]))
+ else if((*pw == tr->langopts.thousands_sep) && (pw[1] == ' ')
+ && iswdigit(pw[2]) && (pw[3] != ' ') && (pw[4] != ' ')) // don't allow only 1 or 2 digits in the final part
{
pw += 2;
ix++; // skip "word"
@@ -2613,65 +3378,75 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
for(n_digits=0; iswdigit(word[n_digits]); n_digits++); // count consecutive digits
- if((n_digits > 4) && (word[0] != '0'))
+ if(n_digits > 4)
{
// word is entirely digits, insert commas and break into 3 digit "words"
number_buf[0] = ' ';
pn = &number_buf[1];
nx = n_digits;
+ nw = 0;
- if((tr->langopts.numbers2 & NUM2_100000a) == NUM2_100000a)
- break_numbers = break_numbers3;
- else
- if(tr->langopts.numbers2 & NUM2_100000)
- break_numbers = break_numbers2;
- else
- break_numbers = break_numbers1;
+ if((n_digits > tr->langopts.max_digits) || (word[0] == '0'))
+ words[ix].flags |= FLAG_INDIVIDUAL_DIGITS;
- while(pn < &number_buf[sizeof(number_buf)-3])
+ while(pn < &number_buf[sizeof(number_buf)-20])
{
- if(!isdigit(c = *pw++) && (c != tr->langopts.decimal_sep))
+ if(!IsDigit09(c = *pw++) && (c != tr->langopts.decimal_sep))
break;
*pn++ = c;
- if((--nx > 0) && (break_numbers & (1 << nx)))
+ nx--;
+ if((nx > 0) && (tr->langopts.break_numbers & (1 << nx)))
{
+ memcpy(&num_wtab[nw++], &words[ix], sizeof(WORD_TAB)); // copy the 'words' entry for each word of numbers
+
if(tr->langopts.thousands_sep != ' ')
{
*pn++ = tr->langopts.thousands_sep;
}
*pn++ = ' ';
- if(break_numbers & (1 << (nx-1)))
- {
- // the next group only has 1 digits (i.e. NUM2_10000), make it three
- *pn++ = '0';
- *pn++ = '0';
- }
- if(break_numbers & (1 << (nx-2)))
+
+ if((words[ix].flags & FLAG_INDIVIDUAL_DIGITS) == 0)
{
- // the next group only has 2 digits (i.e. NUM2_10000), make it three
- *pn++ = '0';
+ if(tr->langopts.break_numbers & (1 << (nx-1)))
+ {
+ // the next group only has 1 digits, make it three
+ *pn++ = '0';
+ *pn++ = '0';
+ }
+ if(tr->langopts.break_numbers & (1 << (nx-2)))
+ {
+ // the next group only has 2 digits (eg. Indian languages), make it three
+ *pn++ = '0';
+ }
}
}
}
- word = pw;
+ pw--;
+ memcpy(&num_wtab[nw], &words[ix], sizeof(WORD_TAB)*2); // the original number word, and the word after it
- // include the next few characters, in case there are an ordinal indicator
- pn[0] = ' ';
- memcpy(pn+1, pw, 8);
- pn[8] = 0;
+ for(j=1; j<=nw; j++)
+ {
+ num_wtab[j].flags &= ~(FLAG_MULTIPLE_SPACES | FLAG_EMBEDDED); // don't use these flags for subsequent parts when splitting a number
+ }
+
+ // include the next few characters, in case there are an ordinal indicator or other suffix
+ memcpy(pn, pw, 16);
+ pn[16] = 0;
+ nw = 0;
for(pw = &number_buf[1]; pw < pn;)
{
- dict_flags = TranslateWord2(tr, pw, &words[ix], words[ix].pre_pause,0 );
+ // keep wflags for each part, for FLAG_HYPHEN_AFTER
+ dict_flags = TranslateWord2(tr, pw, &num_wtab[nw++], words[ix].pre_pause,0 );
while(*pw++ != ' ');
words[ix].pre_pause = 0;
- words[ix].flags = 0;
}
}
else
{
pre_pause = 0;
+
dict_flags = TranslateWord2(tr, word, &words[ix], words[ix].pre_pause, words[ix+1].pre_pause);
if(pre_pause > words[ix+1].pre_pause)
@@ -2693,7 +3468,7 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
}
}
- if((dict_flags & FLAG_DOT) && (ix == word_count-1) && (terminator == CLAUSE_PERIOD))
+ if((dict_flags & (FLAG_ALLOW_DOT | FLAG_NEEDS_DOT)) && (ix == word_count - 1 - dictionary_skipwords) && (terminator & CLAUSE_DOT))
{
// probably an abbreviation such as Mr. or B. rather than end of sentence
clause_pause = 10;
@@ -2703,17 +3478,28 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
if(dict_flags & FLAG_SKIPWORDS)
{
- ix += dictionary_skipwords; // dictionary indicates skip next word(s)
+ // dictionary indicates skip next word(s)
+ while(dictionary_skipwords > 0)
+ {
+ words[ix+dictionary_skipwords].flags |= FLAG_DELETE_WORD;
+ dictionary_skipwords--;
+ }
}
}
+ if(embedded_read < embedded_ix)
+ {
+ // any embedded commands not yet processed?
+ Word_EmbeddedCmd();
+ }
+
for(ix=0; ix<2; ix++)
{
// terminate the clause with 2 PAUSE phonemes
PHONEME_LIST2 *p2;
p2 = &ph_list2[n_ph_list2 + ix];
p2->phcode = phonPAUSE;
- p2->stress = 0;
+ p2->stresslevel = 0;
p2->sourceix = source_index;
p2->synthflags = 0;
}
@@ -2728,18 +3514,22 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
clause_pause = 10;
}
- MakePhonemeList(tr, clause_pause, new_sentence);
+ MakePhonemeList(tr, clause_pause, new_sentence2);
+ phoneme_list[N_PHONEME_LIST].ph = NULL; // recognize end of phoneme_list array, in Generate()
+ phoneme_list[N_PHONEME_LIST].sourceix = 1;
if(embedded_count) // ???? is this needed
{
phoneme_list[n_phoneme_list-2].synthflags = SFLAG_EMBEDDED;
embedded_list[embedded_ix-1] |= 0x80;
+ embedded_list[embedded_ix] = 0x80;
}
prev_clause_pause = clause_pause;
- *tone_out = tone;
+ if(tone_out != NULL)
+ *tone_out = tone;
new_sentence = 0;
if(terminator & CLAUSE_BIT_SENTENCE)
@@ -2752,7 +3542,7 @@ if((c == '/') && (tr->langopts.testing & 2) && IsDigit09(next_in) && IsAlpha(pre
{
// return new voice name if an embedded voice change command terminated the clause
if(terminator & CLAUSE_BIT_VOICE)
- *voice_change = &source[source_index];
+ *voice_change = voice_change_name;
else
*voice_change = NULL;
}
diff --git a/navit/support/espeak/translate.h b/navit/support/espeak/translate.h
index 0556bf280..fe25b191d 100755..100644
--- a/navit/support/espeak/translate.h
+++ b/navit/support/espeak/translate.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -18,18 +18,21 @@
***************************************************************************/
-#define L(c1,c2) (c1<<8)+c2 // combine two characters into an integer for translator name
+#define L(c1,c2) (c1<<8)+c2 // combine two characters into an integer for translator name
#define CTRL_EMBEDDED 0x01 // control character at the start of an embedded command
#define REPLACED_E 'E' // 'e' replaced by silent e
-#define N_WORD_PHONEMES 160 // max phonemes in a word
+#define N_WORD_PHONEMES 200 // max phonemes in a word
#define N_WORD_BYTES 160 // max bytes for the UTF8 characters in a word
#define N_CLAUSE_WORDS 300 // max words in a clause
+#define N_TR_SOURCE 800 // the source text of a single clause (UTF8 bytes)
+
+
#define N_RULE_GROUP2 120 // max num of two-letter rule chains
#define N_HASH_DICT 1024
#define N_CHARSETS 20
-#define N_LETTER_GROUPS 26
+#define N_LETTER_GROUPS 95 // maximum is 127-32
/* dictionary flags, word 1 */
@@ -37,25 +40,25 @@
#define FLAG_SKIPWORDS 0x80
#define FLAG_PREPAUSE 0x100
-#define FLAG_ONLY 0x200
-#define FLAG_ONLY_S 0x400
-#define BITNUM_FLAG_ONLY 9 // bit 9 is set
-#define BITNUM_FLAG_ONLY_S 10 // bit 10 is set
-
-#define FLAG_STRESS_END 0x800 /* full stress if at end of clause */
-#define FLAG_STRESS_END2 0x1000 /* full stress if at end of clause, or only followed by unstressed */
-#define FLAG_UNSTRESS_END 0x2000 /* reduce stress at end of clause */
-#define FLAG_ATEND 0x4000 /* use this pronunciation if at end of clause */
-#define FLAG_SPELLWORD 0x8000 // re-translate the word as individual letters, separated by spaces
-
-#define FLAG_DOT 0x10000 /* ignore '.' after word (abbreviation) */
-#define FLAG_ABBREV 0x20000 // spell as letters, even with a vowel, OR use specified pronunciation rather than split into letters
-#define FLAG_STEM 0x40000 // must have a suffix
-
-#define FLAG_DOUBLING 0x80000 // doubles the following consonant
-#define FLAG_ALT_TRANS 0x100000 // language specific
-#define FLAG_ALT2_TRANS 0x200000 // language specific
-
+#define FLAG_STRESS_END 0x200 // full stress if at end of clause
+#define FLAG_STRESS_END2 0x400 // full stress if at end of clause, or only followed by unstressed
+#define FLAG_UNSTRESS_END 0x800 // reduce stress at end of clause
+#define FLAG_SPELLWORD 0x1000 // re-translate the word as individual letters, separated by spaces
+#define FLAG_ABBREV 0x2000 // spell as letters, even with a vowel, OR use specified pronunciation rather than split into letters
+#define FLAG_DOUBLING 0x4000 // doubles the following consonant
+
+#define BITNUM_FLAG_ALT 14 // bit number of FLAG_ALT_TRANS - 1
+#define FLAG_ALT_TRANS 0x8000 // language specific
+#define FLAG_ALT2_TRANS 0x10000 // language specific
+#define FLAG_ALT3_TRANS 0x20000 // language specific
+#define FLAG_ALT4_TRANS 0x40000 // language specific
+#define FLAG_ALT5_TRANS 0x80000 // language specific
+#define FLAG_ALT6_TRANS 0x100000 // language specific
+
+#define FLAG_COMBINE 0x800000 // combine with the next word
+#define FLAG_ALLOW_DOT 0x01000000 // ignore '.' after word (abbreviation)
+#define FLAG_NEEDS_DOT 0x02000000 // only if the word is followed by a dot
+#define FLAG_WAS_UNPRONOUNCABLE 0x04000000 // the unpronounceable routine was used
#define FLAG_MAX3 0x08000000 // limit to 3 repeats
#define FLAG_PAUSE1 0x10000000 // shorter prepause
#define FLAG_TEXTMODE 0x20000000 // word translates to replacement text, not phonemes
@@ -75,10 +78,21 @@
#define FLAG_VERB_EXT 0x100 /* extend the 'verb follows' */
#define FLAG_CAPITAL 0x200 /* pronunciation if initial letter is upper case */
#define FLAG_ALLCAPS 0x400 // only if the word is all capitals
-#define BITNUM_FLAG_ALLCAPS 0x2a
#define FLAG_ACCENT 0x800 // character name is base-character name + accent name
#define FLAG_HYPHENATED 0x1000 // multiple-words, but needs hyphen between parts 1 and 2
+#define FLAG_SENTENCE 0x2000 // only if the clause is a sentence
+#define FLAG_ONLY 0x4000
+#define FLAG_ONLY_S 0x8000
+#define FLAG_STEM 0x10000 // must have a suffix
+#define FLAG_ATEND 0x20000 // use this pronunciation if at end of clause
+#define FLAG_ATSTART 0x40000 // use this pronunciation if at start of clause
+#define FLAG_NATIVE 0x80000 // not if we've switched translators
+#define FLAG_LOOKUP_SYMBOL 0x40000000 // to indicate called from Lookup()
+
+#define BITNUM_FLAG_ALLCAPS 0x2a
#define BITNUM_FLAG_HYPHENATED 0x2c
+#define BITNUM_FLAG_ONLY 0x2e
+#define BITNUM_FLAG_ONLY_S 0x2f
// wordflags, flags in source word
@@ -98,20 +112,37 @@
#define FLAG_DONT_SWITCH_TRANSLATOR 0x1000
#define FLAG_SUFFIX_REMOVED 0x2000
#define FLAG_HYPHEN_AFTER 0x4000
+#define FLAG_ORDINAL 0x8000 // passed to TranslateNumber() to indicate an ordinal number
+#define FLAG_HAS_DOT 0x10000 // dot after this word
+#define FLAG_COMMA_AFTER 0x20000 // comma after this word
+#define FLAG_MULTIPLE_SPACES 0x40000 // word is preceded by multiple spaces, newline, or tab
+#define FLAG_INDIVIDUAL_DIGITS 0x80000 // speak number as individual digits
+#define FLAG_DELETE_WORD 0x100000 // don't speak this word, it has been spoken as part of the previous word
+#define FLAG_CHAR_REPLACED 0x200000 // characters have been replaced by .replace in the *_rules
+#define FLAG_TRANSLATOR2 0x400000 // retranslating using a different language
+#define FLAG_PREFIX_REMOVED 0x800000 // a prefix has been removed from this word
+
+#define FLAG_SUFFIX_VOWEL 0x08000000 // remember an initial vowel from the suffix
+#define FLAG_NO_TRACE 0x10000000 // passed to TranslateRules() to suppress dictionary lookup printout
+#define FLAG_NO_PREFIX 0x20000000
+#define FLAG_UNPRON_TEST 0x80000000 // do unpronounability test on the beginning of the word
-#define FLAG_NO_TRACE 0x10000 // passed to TranslateRules() to suppress dictionary lookup printout
-#define FLAG_NO_PREFIX 0x20000
// prefix/suffix flags (bits 8 to 14, bits 16 to 22) don't use 0x8000, 0x800000
#define SUFX_E 0x0100 // e may have been added
#define SUFX_I 0x0200 // y may have been changed to i
#define SUFX_P 0x0400 // prefix
#define SUFX_V 0x0800 // suffix means use the verb form pronunciation
-#define SUFX_D 0x1000 // previous letter may have been doubles
+#define SUFX_D 0x1000 // previous letter may have been doubled
#define SUFX_F 0x2000 // verb follows
#define SUFX_Q 0x4000 // don't retranslate
#define SUFX_T 0x10000 // don't affect the stress position in the stem
#define SUFX_B 0x20000 // break, this character breaks the word into stem and suffix (used with SUFX_P)
+#define SUFX_A 0x40000 // remember that the suffix starts with a vowel
+#define SUFX_M 0x80000 // bit 19, allow multiple suffixes
+
+#define SUFX_UNPRON 0x8000 // used to return $unpron flag from *_rules
+
#define FLAG_ALLOW_TEXTMODE 0x02 // allow dictionary to translate to text rather than phonemes
#define FLAG_SUFX 0x04
@@ -127,29 +158,36 @@
#define RULE_CONDITION 5 // followed by condition number (byte)
#define RULE_GROUP_START 6
#define RULE_GROUP_END 7
-#define RULE_LINENUM 8 // next 2 bytes give a line number, for debugging purposes
+#define RULE_PRE_ATSTART 8 // as RULE_PRE but also match with 'start of word'
+#define RULE_LINENUM 9 // next 2 bytes give a line number, for debugging purposes
#define RULE_SPACE 32 // ascii space
-#define RULE_SYLLABLE 9
-#define RULE_STRESSED 10
-#define RULE_DOUBLE 11
-#define RULE_INC_SCORE 12
-#define RULE_DEL_FWD 13
-#define RULE_ENDING 14
+#define RULE_SYLLABLE 21 // @
+#define RULE_STRESSED 10 // &
+#define RULE_DOUBLE 11 // %
+#define RULE_INC_SCORE 12 // +
+#define RULE_DEL_FWD 13 // #
+#define RULE_ENDING 14 // S
#define RULE_DIGIT 15 // D digit
#define RULE_NONALPHA 16 // Z non-alpha
#define RULE_LETTERGP 17 // A B C H F G Y letter group number
#define RULE_LETTERGP2 18 // L + letter group number
-#define RULE_CAPITAL 19 // word starts with a capital letter
+#define RULE_CAPITAL 19 // ! word starts with a capital letter
#define RULE_REPLACEMENTS 20 // section for character replacements
+#define RULE_SKIPCHARS 23 // J
#define RULE_NO_SUFFIX 24 // N
#define RULE_NOTVOWEL 25 // K
#define RULE_IFVERB 26 // V
-#define RULE_ALT1 28 // T word has $alt attribute
+#define RULE_DOLLAR 28 // $ commands
#define RULE_NOVOWELS 29 // X no vowels up to word boundary
#define RULE_SPELLING 31 // W while spelling letter-by-letter
#define RULE_LAST_RULE 31
+#define DOLLAR_UNPR 0x01
+#define DOLLAR_NOPREFIX 0x02
+#define DOLLAR_LIST 0x03
+
+
#define LETTERGP_A 0
#define LETTERGP_B 1
#define LETTERGP_C 2
@@ -161,30 +199,37 @@
// Punctuation types returned by ReadClause()
-// bits 0-7 pause x 10mS, bits 12-14 intonation type,
+// bits 0-11 pause x 10mS
+// bits12-14 intonation type
+// bit 15- don't need space after the punctuation
// bit 19=sentence, bit 18=clause, bits 17=voice change
// bit 16 used to distinguish otherwise identical types
// bit 20= punctuation character can be inside a word (Armenian)
+// bit 21= speak the name of the punctuation character
+// bit 22= dot after the last word
+// bit 23= pause is x 320mS (not x 10mS)
+
#define CLAUSE_BIT_SENTENCE 0x80000
+#define CLAUSE_BIT_CLAUSE 0x40000
#define CLAUSE_BIT_VOICE 0x20000
+#define CLAUSE_BITS_INTONATION 0x7000
#define PUNCT_IN_WORD 0x100000
-
-#define CLAUSE_NONE 0 + 0x04000
-#define CLAUSE_PARAGRAPH 70 + 0x80000
-#define CLAUSE_EOF 35 + 0x90000
-#define CLAUSE_VOICE 0 + 0x24000
-#define CLAUSE_PERIOD 35 + 0x80000
-#define CLAUSE_COMMA 20 + 0x41000
-#define CLAUSE_SHORTCOMMA 4 + 0x41000
-#define CLAUSE_SHORTFALL 4 + 0x40000
-#define CLAUSE_QUESTION 35 + 0x82000
-#define CLAUSE_EXCLAMATION 40 + 0x83000
-#define CLAUSE_COLON 30 + 0x40000
-#ifdef PLATFORM_RISCOS
-#define CLAUSE_SEMICOLON 30 + 0x40000
-#else
-#define CLAUSE_SEMICOLON 30 + 0x41000
-#endif
+#define PUNCT_SAY_NAME 0x200000
+#define CLAUSE_DOT 0x400000
+#define CLAUSE_PAUSE_LONG 0x800000
+
+#define CLAUSE_NONE ( 0 + 0x04000)
+#define CLAUSE_PARAGRAPH (70 + 0x80000)
+#define CLAUSE_EOF (40 + 0x90000)
+#define CLAUSE_VOICE ( 0 + 0x24000)
+#define CLAUSE_PERIOD (40 + 0x80000)
+#define CLAUSE_COMMA (20 + 0x41000)
+#define CLAUSE_SHORTCOMMA ( 4 + 0x41000)
+#define CLAUSE_SHORTFALL ( 4 + 0x40000)
+#define CLAUSE_QUESTION (40 + 0x82000)
+#define CLAUSE_EXCLAMATION (45 + 0x83000)
+#define CLAUSE_COLON (30 + 0x40000)
+#define CLAUSE_SEMICOLON (30 + 0x41000)
#define SAYAS_CHARS 0x12
#define SAYAS_GLYPHS 0x13
@@ -194,6 +239,7 @@
#define SAYAS_DIGITS1 0xc1
#define CHAR_EMPHASIS 0x0530 // this is an unused character code
+#define CHAR_COMMA_BREAK 0x0557 // unused character code
// Rule:
// [4] [match] [1 pre] [2 post] [3 phonemes] 0
@@ -203,32 +249,23 @@
typedef const char * constcharptr;
typedef struct {
- int points;
+ int points;
const char *phonemes;
- int end_type;
+ int end_type;
char *del_fwd;
} MatchRecord;
-
+
// used to mark words with the source[] buffer
typedef struct{
+ unsigned int flags;
unsigned short start;
- unsigned short sourceix;
- unsigned short flags;
unsigned char pre_pause;
unsigned char wmark;
+ unsigned short sourceix;
unsigned char length;
} WORD_TAB;
-// a clause translated into phoneme codes (first stage)
-typedef struct {
- unsigned char phcode;
- unsigned char stress;
- unsigned char tone_number;
- unsigned char synthflags;
- unsigned short sourceix;
-} PHONEME_LIST2;
-
typedef struct {
int type;
@@ -239,8 +276,25 @@ extern PARAM_STACK param_stack[];
extern const int param_defaults[N_SPEECH_PARAM];
-
-#define N_LOPTS 16
+typedef struct {
+ const char *name;
+ int offset;
+ unsigned short range_min, range_max;
+ int language;
+ int flags;
+} ALPHABET;
+
+extern ALPHABET alphabets[];
+extern ALPHABET *current_alphabet;
+// alphabet flags
+#define AL_DONT_NAME 0x01 // don't speak the alphabet name
+#define AL_NOT_LETTERS 0x02 // don't use the language for speaking letters
+#define AL_WORDS 0x04 // use the language to speak words
+#define AL_NOT_CODE 0x08 // don't speak the character code
+#define AL_NO_SYMBOL 0x10 // don't repeat "symbol" or "character"
+
+
+#define N_LOPTS 21
#define LOPT_DIERESES 1
// 1=remove [:] from unstressed syllables, 2= remove from unstressed or non-penultimate syllables
// bit 4=0, if stress < 4, bit 4=1, if not the highest stress in the word
@@ -250,9 +304,12 @@ extern const int param_defaults[N_SPEECH_PARAM];
#define LOPT_PREFIXES 3
// non-zero, change voiced/unoiced to match last consonant in a cluster
- // bit 1=LANG=ru, don't propagate over [v]
+ // bit 0=use regressive voicing
+ // bit 1=LANG=cz,bg don't propagate over [v]
// bit 2=don't propagate acress word boundaries
// bit 3=LANG=pl, propagate over liquids and nasals
+ // bit 4=LANG=cz,sk don't progagate to [v]
+ // bit 8=devoice word-final consonants
#define LOPT_REGRESSIVE_VOICING 4
// 0=default, 1=no check, other allow this character as an extra initial letter (default is 's')
@@ -264,7 +321,7 @@ extern const int param_defaults[N_SPEECH_PARAM];
// increase this to prevent sonorants being shortened before shortened (eg. unstressed) vowels
#define LOPT_SONORANT_MIN 7
- // don't break vowels at word boundary
+ // bit 0: don't break vowels at word boundary
#define LOPT_WORD_MERGE 8
// max. amplitude for vowel at the end of a clause
@@ -292,8 +349,33 @@ extern const int param_defaults[N_SPEECH_PARAM];
#define LOPT_IT_DOUBLING 14
// Call ApplySpecialAttributes() if $alt or $alt2 is set for a word
+ // bit 1: stressed syllable: $alt change [e],[o] to [E],[O], $alt2 change [E],[O] to [e],[o]
#define LOPT_ALT 15
+ // pause for bracket (default=4), pause when annoucing bracket names (default=2)
+#define LOPT_BRACKET_PAUSE 16
+
+ // bit 1, don't break clause before annoucning . ? !
+#define LOPT_ANNOUNCE_PUNCT 17
+
+ // recognize long vowels (0 = don't recognize)
+#define LOPT_LONG_VOWEL_THRESHOLD 18
+
+ // bit 0: Don't allow suffices if there is no previous syllable
+#define LOPT_SUFFIX 19
+
+ // bit 0 Apostrophe at start of word is part of the word
+ // bit 1 Apostrophe at end of word is part of the word
+#define LOPT_APOSTROPHE 20
+
+
+// stress_rule
+#define STRESSPOSN_1L 0 // 1st syllable
+#define STRESSPOSN_2L 1 // 2nd syllable
+#define STRESSPOSN_2R 2 // penultimate
+#define STRESSPOSN_1R 3 // final syllable
+#define STRESSPOSN_3R 4 // antipenultimate
+
typedef struct {
// bits0-2 separate words with (1=pause_vshort, 2=pause_short, 3=pause, 4=pause_long 5=[?] phonemme)
@@ -304,34 +386,99 @@ typedef struct {
int vowel_pause;
int stress_rule; // 1=first syllable, 2=penultimate, 3=last
-// bit0=don't stress monosyllables, except at end of clause
+#define S_NO_DIM 0x02
+#define S_FINAL_DIM 0x04
+#define S_FINAL_DIM_ONLY 0x06
// bit1=don't set diminished stress,
// bit2=mark unstressed final syllables as diminished
+
+// bit3=set consecutive unstressed syllables in unstressed words to diminished, but not in stressed words
+
+#define S_FINAL_NO_2 0x10
// bit4=don't allow secondary stress on last syllable
+
+#define S_NO_AUTO_2 0x20
// bit5-don't use automatic secondary stress
+
+#define S_2_TO_HEAVY 0x40
// bit6=light syllable followed by heavy, move secondary stress to the heavy syllable. LANG=Finnish
+
+#define S_FIRST_PRIMARY 0x80
+// bit7=if more than one primary stress, make the subsequent primaries to secondary stress
+
+#define S_FINAL_STRESS_C 0x100
// bit8=stress last syllable if it doesn't end in a vowel
+
+#define S_FINAL_SPANISH 0x200
// bit9=stress last syllable if it doesn't end in vowel or "s" or "n" LANG=Spanish
+
+#define S_2_SYL_2 0x1000
// bit12= In a 2-syllable word, if one has primary stress then give the other secondary stress
+
+#define S_INITIAL_2 0x2000
// bit13= If there is only one syllable before the primary stress, give it a secondary stress
-// bit15= Give stress to the first unstressed syllable
-// bit16= Don't diminish consecutive syllables within a word.
+
+#define S_MID_DIM 0x10000
+// bit 16= Set (not first or last) syllables to diminished stress
+
+#define S_PRIORITY_STRESS 0x20000
// bit17= "priority" stress reduces other primary stress to "unstressed" not "secondary"
+
+#define S_EO_CLAUSE1 0x40000
// bit18= don't lengthen short vowels more than long vowels at end-of-clause
+
+#define S_FINAL_LONG 0x80000
// bit19=stress on final syllable if it has a long vowel, but previous syllable has a short vowel
- int stress_flags;
+
+#define S_HYPEN_UNSTRESS 0x100000
+// bit20= hyphenated words, 2nd part is unstressed
+
+#define S_NO_EOC_LENGTHEN 0x200000
+// bit21= don't lengthen vowels at end-of-clause
+
+// bit15= Give stress to the first unstressed syllable
+
+
+ int stress_flags;
int unstressed_wd1; // stress for $u word of 1 syllable
int unstressed_wd2; // stress for $u word of >1 syllable
int param[N_LOPTS];
+ int param2[N_LOPTS];
unsigned char *length_mods;
unsigned char *length_mods0;
-#define NUM_ROMAN 0x20000
-#define NUM_ROMAN_UC 0x40000
-#define NUM_NOPAUSE 0x80000
-#define NUM_ROMAN_AFTER 0x200000
-#define NUM_VIGESIMAL 0x400000
+#define NUM_THOUS_SPACE 0x4
+#define NUM_DECIMAL_COMMA 0x8
+#define NUM_SWAP_TENS 0x10
+#define NUM_AND_UNITS 0x20
+#define NUM_HUNDRED_AND 0x40
+#define NUM_SINGLE_AND 0x80
+#define NUM_SINGLE_STRESS 0x100
+#define NUM_SINGLE_VOWEL 0x200
+#define NUM_OMIT_1_HUNDRED 0x400
+#define NUM_1900 0x800
+#define NUM_ALLOW_SPACE 0x1000
+#define NUM_DFRACTION_1 0x2000
+#define NUM_DFRACTION_2 0x4000
+#define NUM_DFRACTION_3 0x6000
+#define NUM_DFRACTION_4 0x8000
+#define NUM_DFRACTION_5 0xa000
+#define NUM_DFRACTION_6 0xc000
+#define NUM_DFRACTION_7 0xe000 // lang=si, alternative form of number for decimal fraction digits (except the last)
+#define NUM_ORDINAL_DOT 0x10000
+#define NUM_NOPAUSE 0x20000
+#define NUM_AND_HUNDRED 0x40000
+#define NUM_THOUSAND_AND 0x80000
+#define NUM_VIGESIMAL 0x100000
+#define NUM_OMIT_1_THOUSAND 0x200000
+#define NUM_ZERO_HUNDRED 0x400000
+#define NUM_HUNDRED_AND_DIGIT 0x800000
+#define NUM_ROMAN 0x1000000
+#define NUM_ROMAN_CAPITALS 0x2000000
+#define NUM_ROMAN_AFTER 0x4000000
+#define NUM_ROMAN_ORDINAL 0x8000000
+#define NUM_SINGLE_STRESS_L 0x10000000
// bits0-1=which numbers routine to use.
// bit2= thousands separator must be space
@@ -346,35 +493,66 @@ typedef struct {
// bit11=say 19** as nineteen hundred
// bit12=allow space as thousands separator (in addition to langopts.thousands_sep)
// bits13-15 post-decimal-digits 0=single digits, 1=(LANG=it) 2=(LANG=pl) 3=(LANG=ro)
- // bit16=dot after number indicates ordinal
- // bit17=recognize roman numbers
- // bit18=Roman numbers only if upper case
- // bit19=don't add pause after a number
- // bit20='and' before hundreds
- // bit21= say "roman" after the number, not before
- // bit22= vigesimal number, if tens are not found
+
+ // bit16= dot after number indicates ordinal
+ // bit17= don't add pause after a number
+ // bit18= 'and' before hundreds
+ // bit19= 'and' after thousands if there are no hundreds
+ // bit20= vigesimal number, if tens are not found
+ // bit21= omit "one" before "thousand"
+ // bit22= say "zero" before hundred
+ // bit23= add "and" after hundreds and thousands, only if there are digits and no tens
+
+ // bit24= recognize roman numbers
+ // bit25= Roman numbers only if upper case
+ // bit26= say "roman" after the number, not before
+ // bit27= Roman numbers are ordinal numbers
+ // bit28= only one primary stress in tens+units (on the tens)
int numbers;
-#define NUM2_100000 0x800 // numbers for 100,000 and 10,000,000
-#define NUM2_100000a 0xc00 // numbers for 100,000 and 1,000,000
+#define NUM2_THOUSANDS_VAR1 0x40
+#define NUM2_THOUSANDS_VAR2 0x80
+#define NUM2_THOUSANDS_VAR3 0xc0
+#define NUM2_THOUSANDS_VAR4 0x100
+#define NUM2_THOUSANDS_VAR5 0x140
+
+#define NUM2_ORDINAL_NO_AND 0x800
+#define NUM2_MULTIPLE_ORDINAL 0x1000
+#define NUM2_NO_TEEN_ORDINALS 0x2000
+#define NUM2_MYRIADS 0x4000
+#define NUM2_ENGLISH_NUMERALS 0x8000
+#define NUM2_PERCENT_BEFORE 0x10000
+#define NUM2_OMIT_1_HUNDRED_ONLY 0x20000
+#define NUM2_ORDINAL_AND_THOUSANDS 0x40000
// bits 1-4 use variant form of numbers before thousands,millions,etc.
- // bit6=(LANG=pl) two forms of plural, M or MA
- // bit7=(LANG-ru) use MB for 1 thousand, million, etc
- // bit8=(LANG=cs,sk) two forms of plural, M or MA
+ // bits 6-8 use different forms of thousand, million, etc (M MA MB)
// bit9=(LANG=rw) say "thousand" and "million" before its number, not after
- // bit10=(LANG=sw) special word for 100,000 and 1,000,000
- // bit11=(LANG=hi) special word for 100,000 and 10,000,000
+ // bit11=(LANG=es,an) don't say 'and' between tens and units for ordinal numbers
+ // bit12=(LANG=el,es) use ordinal form of hundreds and tens as well as units
+ // bit13=(LANG=pt) don't use 11-19 numbers to make ordinals
+ // bit14=(LANG=ko) use myriads (groups of 4 digits) not thousands (groups of 3)
+ // bit15=(LANG=ne) speak (non-replaced) English numerals in English
+ // bit16=(LANG=si) say "%" before the number
+ // bit17=(LANG=ml) omit "one" before hundred only if there are no previous digits
+ // bit18=(LANG=ta) same variant for ordinals and thousands (#o = #a)
int numbers2;
+#define BREAK_THOUSANDS 0x49249248
+ int break_numbers; // which digits to break the number into thousands, millions, etc (Hindi has 100,000 not 1,000,000)
int max_roman;
+ int min_roman;
int thousands_sep;
int decimal_sep;
+ int max_digits; // max number of digits which can be spoken as an integer number (rather than individual digits)
+ const char *ordinal_indicator; // UTF-8 string
+ const char *roman_suffix; // add this (ordinal) suffix to Roman numbers (LANG=an)
// bit 0, accent name before the letter name, bit 1 "capital" after letter name
int accents;
int tone_language; // 1=tone language
int intonation_group;
+ unsigned char tunes[6];
int long_stop; // extra mS pause for a lengthened stop
int phoneme_change; // TEST, change phonemes, after translation
char max_initial_consonants;
@@ -382,10 +560,21 @@ typedef struct {
char tone_numbers;
char ideographs; // treat as separate words
char textmode; // the meaning of FLAG_TEXTMODE is reversed (to save data when *_list file is compiled)
+ char dotless_i; // uses letter U+0131
int testing; // testing options: bit 1= specify stressed syllable in the form: "outdoor/2"
int listx; // compile *_listx after *list
const unsigned int *replace_chars; // characters to be substitutes
- const char *ascii_language; // switch to this language for Latin characters
+ char ascii_language[8]; // switch to this language for Latin characters
+ int our_alphabet; // offset for main alphabet (if not set in letter_bits_offset)
+ int alt_alphabet; // offset for another language to recognize
+ int alt_alphabet_lang; // language for the alt_alphabet
+ int max_lengthmod;
+ int lengthen_tonic; // lengthen the tonic syllable
+ int suffix_add_e; // replace a suffix (which has the SUFX_E flag) with this character
+
+#define DICTDIALECT_EN_US 1 // bit number
+#define DICTDIALECT_ES_LA 2
+ int dict_dialect; // bitmap, use a dialect for foreign words
} LANGUAGE_OPTIONS;
@@ -401,30 +590,29 @@ typedef struct {
-#define NUM_SEP_DOT 0x0008 // . , for thousands and decimal separator
-#define NUM_SEP_SPACE 0x1000 // allow space as thousands separator (in addition to langopts.thousands_sep)
-#define NUM_DEC_IT 0x2000 // (LANG=it) speak post-decimal-point digits as a combined number not as single digits
-
-typedef struct Translator
-{//=============
+typedef struct
+{//===========
LANGUAGE_OPTIONS langopts;
int translator_name;
- int transpose_offset;
int transpose_max;
int transpose_min;
+ const char *transpose_map;
+ char dictionary_name[40];
- char phon_out[300];
char phonemes_repeat[20];
- int phonemes_repeat_count;
+ int phonemes_repeat_count;
+ int phoneme_tab_ix;
unsigned char stress_amps[8];
unsigned char stress_amps_r[8];
short stress_lengths[8];
int dict_condition; // conditional apply some pronunciation rules and dict.lookups
+ int dict_min_size;
const unsigned short *charset_a0; // unicodes for characters 0xa0 to oxff
const wchar_t *char_plus_apostrophe; // single chars + apostrophe treated as words
const wchar_t *punct_within_word; // allow these punctuation characters within words
+ const unsigned short *chars_ignore;
// holds properties of characters: vowel, consonant, etc for pronunciation rules
unsigned char letter_bits[256];
@@ -443,16 +631,17 @@ typedef struct Translator
// groups1 and groups2 are indexes into data_dictrules, set up by InitGroups()
// the two-letter rules for each letter must be consecutive in the language_rules source
-
+
char *groups1[256]; // translation rule lists, index by single letter
+ char *groups3[128]; // index by offset letter
char *groups2[N_RULE_GROUP2]; // translation rule lists, indexed by two-letter pairs
unsigned int groups2_name[N_RULE_GROUP2]; // the two letter pairs for groups2[]
int n_groups2; // number of groups2[] entries used
-
+
unsigned char groups2_count[256]; // number of 2 letter groups for this initial letter
unsigned char groups2_start[256]; // index into groups2
-
-
+ const short *frequent_pairs; // list of frequent pairs of letters, for use in compressed *_list
+
int expect_verb;
int expect_past; // expect past tense
int expect_verb_s;
@@ -462,14 +651,15 @@ typedef struct Translator
int word_vowel_count; // number of vowels so far
int word_stressed_count; // number of vowels so far which could be stressed
-
+
int clause_upper_count; // number of upper case letters in the clause
int clause_lower_count; // number of lower case letters in the clause
int prepause_timeout;
int end_stressed_vowel; // word ends with stressed vowel
- int prev_dict_flags; // dictionary flags from previous word
-} Translator; // end of class Translator
+ int prev_dict_flags[2]; // dictionary flags from previous word
+ int clause_terminator;
+} Translator;
extern int option_tone2;
@@ -479,6 +669,7 @@ extern int option_tone_flags;
extern int option_waveout;
extern int option_quiet;
extern int option_phonemes;
+extern int option_mbrola_phonemes;
extern int option_phoneme_events;
extern int option_linelength; // treat lines shorter than this as end-of-clause
extern int option_multibyte;
@@ -513,8 +704,8 @@ extern char skip_marker[N_MARKER_LENGTH];
extern wchar_t option_punctlist[N_PUNCTLIST]; // which punctuation characters to announce
extern unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS];
-extern struct Translator *translator;
-extern struct Translator *translator2;
+extern Translator *translator;
+extern Translator *translator2;
extern const unsigned short *charsets[N_CHARSETS];
extern char dictionary_name[40];
extern char ctrl_embedded; // to allow an alternative CTRL for embedded commands
@@ -524,15 +715,16 @@ extern int dictionary_skipwords;
extern int (* uri_callback)(int, const char *, const char *);
extern int (* phoneme_callback)(const char *);
-extern void SetLengthMods(struct Translator *tr, int value);
+extern void SetLengthMods(Translator *tr, int value);
void LoadConfig(void);
-int TransposeAlphabet(char *text, int offset, int min, int max);
+int TransposeAlphabet(Translator *tr, char *text);
int utf8_in(int *c, const char *buf);
int utf8_in2(int *c, const char *buf, int backwards);
int utf8_out(unsigned int c, char *buf);
int utf8_nbytes(const char *buf);
int lookupwchar(const unsigned short *list,int c);
+int lookupwchar2(const unsigned short *list,int c);
int Eof(void);
char *strchr_w(const char *s, int c);
int IsBracket(int c);
@@ -540,23 +732,35 @@ void InitNamedata(void);
void InitText(int flags);
void InitText2(void);
int IsDigit(unsigned int c);
+int IsDigit09(unsigned int c);
int IsAlpha(unsigned int c);
+int IsVowel(Translator *tr, int c);
+int IsSuperscript(int letter);
+int iswalpha2(int c);
int isspace2(unsigned int c);
+int iswlower2(int c);
+int iswupper2(int c);
int towlower2(unsigned int c);
-void GetTranslatedPhonemeString(char *phon_out, int n_phon_out);
+int towupper2(unsigned int c);
+const char *GetTranslatedPhonemeString(int phoneme_mode);
+const char *WordToString2(unsigned int word);
+ALPHABET *AlphabetFromChar(int c);
+ALPHABET *AlphabetFromName(const char *name);
-struct Translator *SelectTranslator(const char *name);
+Translator *SelectTranslator(const char *name);
int SetTranslator2(const char *name);
-void DeleteTranslator(struct Translator *tr);
-int Lookup(struct Translator *tr, const char *word, char *ph_out);
+void DeleteTranslator(Translator *tr);
+void ProcessLanguageOptions(LANGUAGE_OPTIONS *langopts);
+int Lookup(Translator *tr, const char *word, char *ph_out);
+int LookupFlags(Translator *tr, const char *word, unsigned int **flags_out);
-int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, int wflags);
-int TranslateRoman(Translator *tr, char *word, char *ph_out);
+int TranslateNumber(Translator *tr, char *word1, char *ph_out, unsigned int *flags, WORD_TAB *wtab, int control);
+int TranslateRoman(Translator *tr, char *word, char *ph_out, WORD_TAB *wtab);
void ChangeWordStress(Translator *tr, char *word, int new_stress);
void SetSpellingStress(Translator *tr, char *phonemes, int control, int n_chars);
-int TranslateLetter(Translator *tr, char *letter, char *phonemes, int control, int word_length);
-void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf);
+int TranslateLetter(Translator *tr, char *letter, char *phonemes, int control);
+void LookupLetter(Translator *tr, unsigned int letter, int next_byte, char *ph_buf, int control);
void LookupAccentedLetter(Translator *tr, unsigned int letter, char *ph_buf);
int LoadDictionary(Translator *tr, const char *name, int no_error);
@@ -564,7 +768,6 @@ int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *f
void MakePhonemeList(Translator *tr, int post_pause, int new_sentence);
int ChangePhonemes_ru(Translator *tr, PHONEME_LIST2 *phlist, int n_ph, int index, PHONEME_TAB *ph, CHANGEPH *ch);
-void ApplySpecialAttribute(Translator *tr, char *phonemes, int dict_flags);
void ApplySpecialAttribute2(Translator *tr, char *phonemes, int dict_flags);
void AppendPhonemes(Translator *tr, char *string, int size, const char *ph);
@@ -572,13 +775,18 @@ void CalcLengths(Translator *tr);
void CalcPitches(Translator *tr, int clause_tone);
int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy);
-int Unpronouncable(Translator *tr, char *word);
+int Unpronouncable(Translator *tr, char *word, int posn);
void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags, int tonic, int prev_stress);
int TranslateRules(Translator *tr, char *p, char *phonemes, int size, char *end_phonemes, int end_flags, unsigned int *dict_flags);
-int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab);
+int TranslateWord(Translator *tr, char *word1, int next_pause, WORD_TAB *wtab, char *word_out);
void *TranslateClause(Translator *tr, FILE *f_text, const void *vp_input, int *tone, char **voice_change);
-int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix_top, int n_buf, int *tone_type);
+int ReadClause(Translator *tr, FILE *f_in, char *buf, short *charix, int *charix_top, int n_buf, int *tone_type, char *voice_change);
-void SetVoiceStack(espeak_VOICE *v);
+void SetVoiceStack(espeak_VOICE *v, const char *variant_name);
+void InterpretPhoneme(Translator *tr, int control, PHONEME_LIST *plist, PHONEME_DATA *phdata, WORD_PH_DATA *worddata);
+void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata);
+char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags);
extern FILE *f_trans; // for logging
+extern FILE *f_logespeak;
+extern int logging_type; // from config file
diff --git a/navit/support/espeak/voice.h b/navit/support/espeak/voice.h
index ab69fa128..36be098f1 100644
--- a/navit/support/espeak/voice.h
+++ b/navit/support/espeak/voice.h
@@ -21,15 +21,17 @@
typedef struct {
char v_name[40];
+ char language_name[20];
int phoneme_tab_ix; // phoneme table number
int pitch_base; // Hz<<12
int pitch_range; // standard = 0x1000
-
+
int speedf1;
int speedf2;
int speedf3;
+ int speed_percent; // adjust the WPM speed by this percentage
int flutter;
int roughness;
int echo_delay;
@@ -40,6 +42,7 @@ typedef struct {
int formant_factor; // adjust nominal formant frequencies by this because of the voice's pitch (256ths)
int consonant_amp; // amplitude of unvoiced consonants
int consonant_ampv; // amplitude of the noise component of voiced consonants
+ int samplerate;
int klattv[8];
// parameters used by Wavegen
@@ -67,6 +70,7 @@ typedef struct {
// percentages shown to user, ix=N_PEAKS means ALL peaks
extern USHORT voice_pcnt[N_PEAKS+1][3];
+extern espeak_VOICE current_voice_selected;
extern voice_t *voice;
extern int tone_points[12];
@@ -76,6 +80,8 @@ espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name);
voice_t *LoadVoice(const char *voice_name, int control);
voice_t *LoadVoiceVariant(const char *voice_name, int variant);
void DoVoiceChange(voice_t *v);
+void WVoiceChanged(voice_t *wvoice);
void WavegenSetVoice(voice_t *v);
void ReadTonePoints(char *string, int *tone_pts);
+void VoiceReset(int control);
diff --git a/navit/support/espeak/voices.c b/navit/support/espeak/voices.c
index a7bd3f5a0..c0279af9f 100755..100644
--- a/navit/support/espeak/voices.c
+++ b/navit/support/espeak/voices.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2014 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -42,15 +42,14 @@
#include "voice.h"
#include "translate.h"
-#define int(x) ((int)(x))
-#define double(x) ((double)(x))
-#define __cdecl
+#include "voices.h"
MNEM_TAB genders [] = {
{"unknown", 0},
{"male", 1},
{"female", 2},
- {NULL, 0 }};
+ {NULL, 0 }
+};
int tone_points[12] = {600,170, 1200,135, 2000,110, 3000,110, -1,0};
//int tone_points[12] = {250,200, 400,170, 600,170, 1200,135, 2000,110, -1,0};
@@ -64,12 +63,12 @@ int formant_rate[9]; // values adjusted for actual sample rate
#define DEFAULT_LANGUAGE_PRIORITY 5
-#define N_VOICES_LIST 150
+#define N_VOICES_LIST 250
static int n_voices_list = 0;
static espeak_VOICE *voices_list[N_VOICES_LIST];
static int len_path_voices;
-espeak_VOICE voice_selected;
+espeak_VOICE current_voice_selected;
enum {
@@ -95,11 +94,13 @@ enum {
// these override defaults set by the translator
V_WORDGAP,
V_INTONATION,
+ V_TUNES,
V_STRESSLENGTH,
V_STRESSAMP,
V_STRESSADD,
V_DICTRULES,
V_STRESSRULE,
+ V_STRESSOPT,
V_CHARSET,
V_NUMBERS,
V_OPTION,
@@ -107,6 +108,10 @@ enum {
V_MBROLA,
V_KLATT,
V_FAST,
+ V_SPEED,
+ V_DICTMIN,
+ V_ALPHABET2,
+ V_DICTDIALECT,
// these need a phoneme table to have been specified
V_REPLACE,
@@ -115,12 +120,13 @@ enum {
-typedef struct {
- const char *mnem;
- int data;
-} keywtab_t;
+static MNEM_TAB options_tab[] = {
+ {"reduce_t", LOPT_REDUCE_T},
+ {"bracket", LOPT_BRACKET_PAUSE},
+ {NULL, -1}
+};
-static keywtab_t keyword_tab[] = {
+static MNEM_TAB keyword_tab[] = {
{"name", V_NAME},
{"language", V_LANGUAGE},
{"gender", V_GENDER},
@@ -128,14 +134,16 @@ static keywtab_t keyword_tab[] = {
{"formant", V_FORMANT},
{"pitch", V_PITCH},
{"phonemes", V_PHONEMES},
- {"translator", V_TRANSLATOR},
+ {"translator", V_TRANSLATOR},
{"dictionary", V_DICTIONARY},
{"stressLength", V_STRESSLENGTH},
{"stressAmp", V_STRESSAMP},
{"stressAdd", V_STRESSADD},
{"intonation", V_INTONATION},
+ {"tunes", V_TUNES},
{"dictrules", V_DICTRULES},
{"stressrule", V_STRESSRULE},
+ {"stressopt", V_STRESSOPT},
{"charset", V_CHARSET},
{"replace", V_REPLACE},
{"words", V_WORDGAP},
@@ -152,7 +160,11 @@ static keywtab_t keyword_tab[] = {
{"mbrola", V_MBROLA},
{"consonants", V_CONSONANTS},
{"klatt", V_KLATT},
- {"fast_test", V_FAST},
+ {"fast_test2", V_FAST},
+ {"speed", V_SPEED},
+ {"dict_min", V_DICTMIN},
+ {"alphabet2", V_ALPHABET2},
+ {"dictdialect", V_DICTDIALECT},
// these just set a value in langopts.param[]
{"l_dieresis", 0x100+LOPT_DIERESES},
@@ -162,11 +174,20 @@ static keywtab_t keyword_tab[] = {
{"l_unpronouncable", 0x100+LOPT_UNPRONOUNCABLE},
{"l_sonorant_min", 0x100+LOPT_SONORANT_MIN},
{"l_length_mods", 0x100+LOPT_LENGTH_MODS},
- {NULL, 0} };
+ {"apostrophe", 0x100+LOPT_APOSTROPHE},
+ {NULL, 0}
+};
+
+static MNEM_TAB dict_dialects[] = {
+ {"en-us", DICTDIALECT_EN_US},
+ {"es-la", DICTDIALECT_ES_LA},
+ {NULL, 0}
+};
+
#define N_VOICE_VARIANTS 12
const char variants_either[N_VOICE_VARIANTS] = {1,2,12,3,13,4,14,5,11,0};
-const char variants_male[N_VOICE_VARIANTS] = {1,2,3,4,5,0};
+const char variants_male[N_VOICE_VARIANTS] = {1,2,3,4,5,6,0};
const char variants_female[N_VOICE_VARIANTS] = {11,12,13,14,0};
const char *variant_lists[3] = {variants_either, variants_male, variants_female};
@@ -183,6 +204,12 @@ static char *fgets_strip(char *buf, int size, FILE *f_in)
if(fgets(buf,size,f_in) == NULL)
return(NULL);
+ if(buf[0] == '#')
+ {
+ buf[0] = 0;
+ return(buf);
+ }
+
len = strlen(buf);
while((--len > 0) && isspace(buf[len]))
buf[len] = 0;
@@ -194,6 +221,20 @@ static char *fgets_strip(char *buf, int size, FILE *f_in)
}
+static int LookupTune(const char *name)
+{//====================================
+ int ix;
+
+ for(ix=0; ix<n_tunes; ix++)
+ {
+ if(strcmp(name, tunes[ix].name) == 0)
+ return(ix);
+ }
+ return(-1);
+} // end of LookupTune
+
+
+
static void SetToneAdjust(voice_t *voice, int *tone_pts)
{//=====================================================
int ix;
@@ -217,11 +258,11 @@ static void SetToneAdjust(voice_t *voice, int *tone_pts)
height2 = tone_pts[pt+1];
if((freq2 - freq1) > 0)
{
- rate = double(height2-height1)/(freq2-freq1);
+ rate = (double)(height2-height1)/(freq2-freq1);
for(ix=freq1; ix<freq2; ix++)
{
- y = height1 + int(rate * (ix-freq1));
+ y = height1 + (int)(rate * (ix-freq1));
if(y > 255)
y = 255;
voice->tone_adjust[ix] = y;
@@ -242,9 +283,9 @@ void ReadTonePoints(char *string, int *tone_pts)
tone_pts[ix] = -1;
sscanf(string,"%d %d %d %d %d %d %d %d %d %d",
- &tone_pts[0],&tone_pts[1],&tone_pts[2],&tone_pts[3],
- &tone_pts[4],&tone_pts[5],&tone_pts[6],&tone_pts[7],
- &tone_pts[8],&tone_pts[9]);
+ &tone_pts[0],&tone_pts[1],&tone_pts[2],&tone_pts[3],
+ &tone_pts[4],&tone_pts[5],&tone_pts[6],&tone_pts[7],
+ &tone_pts[8],&tone_pts[9]);
}
@@ -269,7 +310,7 @@ static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char*lea
espeak_VOICE *voice_data;
int priority;
int age;
- int n_variants = 3; // default, number of variants of this voice before using another voice
+ int n_variants = 4; // default, number of variants of this voice before using another voice
int gender;
#ifdef PLATFORM_WINDOWS
@@ -298,8 +339,7 @@ static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char*lea
while(isspace(*p)) p++;
strncpy0(vname,p,sizeof(vname));
}
- else
- if(memcmp(linebuf,"language",8)==0)
+ else if(memcmp(linebuf,"language",8)==0)
{
priority = DEFAULT_LANGUAGE_PRIORITY;
vlanguage[0] = 0;
@@ -316,13 +356,11 @@ static espeak_VOICE *ReadVoiceFile(FILE *f_in, const char *fname, const char*lea
n_languages++;
}
}
- else
- if(memcmp(linebuf,"gender",6)==0)
+ else if(memcmp(linebuf,"gender",6)==0)
{
sscanf(&linebuf[6],"%s %d",vgender,&age);
}
- else
- if(memcmp(linebuf,"variants",8)==0)
+ else if(memcmp(linebuf,"variants",8)==0)
{
sscanf(&linebuf[8],"%d",&n_variants);
}
@@ -369,29 +407,38 @@ void VoiceReset(int tone_only)
// Set voice to the default values
int pk;
- static unsigned char default_heights[N_PEAKS] = {128,128,120,120,110,110,128,128,128};
- static unsigned char default_widths[N_PEAKS] = {128,128,128,160,171,171,128,128,128};
+ static unsigned char default_heights[N_PEAKS] = {130,128,120,116,100,100,128,128,128}; // changed for v.1.47
+ static unsigned char default_widths[N_PEAKS] = {140,128,128,160,171,171,128,128,128};
+// static unsigned char default_heights[N_PEAKS] = {128,128,120,120,110,110,128,128,128}; // previous version
+// static unsigned char default_widths[N_PEAKS] = {128,128,128,160,171,171,128,128,128};
static int breath_widths[N_PEAKS] = {0,200,200,400,400,400,600,600,600};
- // default is: pitch 82,118
-// voice->pitch_base = 0x49000; // default, 73 << 12;
-// voice->pitch_range = 0x0f30; // default = 0x1000
+ // default is: pitch 80,118
voice->pitch_base = 0x47000;
- voice->pitch_range = 3996;
+ voice->pitch_range = 4104;
+
+// default is: pitch 80,117
+// voice->pitch_base = 0x47000;
+// voice->pitch_range = 3996;
voice->formant_factor = 256;
+ voice->speed_percent = 100;
voice->echo_delay = 0;
voice->echo_amp = 0;
voice->flutter = 64;
voice->n_harmonic_peaks = 5;
voice->peak_shape = 0;
voice->voicing = 64;
- voice->consonant_amp = 100;
+ voice->consonant_amp = 90; // change from 100 to 90 for v.1.47
voice->consonant_ampv = 100;
+ voice->samplerate = samplerate_native;
memset(voice->klattv,0,sizeof(voice->klattv));
- memset(speed.fast_settings,0,sizeof(speed.fast_settings));
+
+ speed.fast_settings[0] = 450;
+ speed.fast_settings[1] = 800;
+ speed.fast_settings[2] = 175;
#ifdef PLATFORM_RISCOS
voice->roughness = 1;
@@ -412,13 +459,12 @@ void VoiceReset(int tone_only)
// adjust formant smoothing depending on sample rate
formant_rate[pk] = (formant_rate_22050[pk] * 22050)/samplerate;
}
- voice->height[2] = 240; // reduce F2 slightly
// This table provides the opportunity for tone control.
// Adjustment of harmonic amplitudes, steps of 8Hz
// value of 128 means no change
// memset(voice->tone_adjust,128,sizeof(voice->tone_adjust));
-SetToneAdjust(voice,tone_points);
+ SetToneAdjust(voice,tone_points);
// default values of speed factors
voice->speedf1 = 256;
@@ -452,11 +498,11 @@ static void VoiceFormant(char *p)
return;
if(freq >= 0)
- voice->freq[formant] = int(freq * 2.56001);
+ voice->freq[formant] = (int)(freq * 2.56001);
if(height >= 0)
- voice->height[formant] = int(height * 2.56001);
+ voice->height[formant] = (int)(height * 2.56001);
if(width >= 0)
- voice->width[formant] = int(width * 2.56001);
+ voice->width[formant] = (int)(width * 2.56001);
voice->freqadd[formant] = freqadd;
}
@@ -490,8 +536,24 @@ static void PhonemeReplacement(int type, char *p)
static int Read8Numbers(char *data_in,int *data)
{//=============================================
// Read 8 integer numbers
+ memset(data, 0, 8+sizeof(int));
return(sscanf(data_in,"%d %d %d %d %d %d %d %d",
- &data[0],&data[1],&data[2],&data[3],&data[4],&data[5],&data[6],&data[7]));
+ &data[0],&data[1],&data[2],&data[3],&data[4],&data[5],&data[6],&data[7]));
+}
+
+
+static unsigned int StringToWord2(const char *string)
+{//======================================================
+// Convert a language name string to a word such as L('e','n')
+ int ix;
+ int c;
+ unsigned int value = 0;
+
+ for(ix=0; (ix<4) && ((c = string[ix]) != 0); ix++)
+ {
+ value = (value << 8) | (c & 0xff);
+ }
+ return(value);
}
@@ -503,13 +565,12 @@ voice_t *LoadVoice(const char *vname, int control)
// bit 4 1 = vname = full path
FILE *f_voice = NULL;
- keywtab_t *k;
char *p;
int key;
int ix;
int n;
int value;
- int error = 0;
+ int value2;
int langix = 0;
int tone_only = control & 2;
int language_set = 0;
@@ -527,26 +588,35 @@ voice_t *LoadVoice(const char *vname, int control)
char translator_name[40];
char new_dictionary[40];
char phonemes_name[40];
+ char option_name[40];
const char *language_type;
- char buf[200];
+ char buf[sizeof(path_home)+30];
char path_voices[sizeof(path_home)+12];
- char langname[4];
+ int dict_min = 0;
int stress_amps[8];
int stress_lengths[8];
int stress_add[8];
+ char names[8][40];
+ char name1[40];
+ char name2[80];
+ const char *voice_dir;
int pitch1;
int pitch2;
- static char voice_identifier[40]; // file name for voice_selected
- static char voice_name[40]; // voice name for voice_selected
- static char voice_languages[100]; // list of languages and priorities for voice_selected
+ static char voice_identifier[40]; // file name for current_voice_selected
+ static char voice_name[40]; // voice name for current_voice_selected
+ static char voice_languages[100]; // list of languages and priorities for current_voice_selected
- strcpy(voicename,vname);
- if(voicename[0]==0)
- strcpy(voicename,"default");
+ // which directory to look for a named voice. List of voice names, must end in a space.
+ static const char *voices_asia =
+ "az bn fa fa-pin hi hy hy-west id ka kn ku ml ms ne pa ta te tr vi vi-hue vi-sgn zh zh-yue ";
+ static const char *voices_europe =
+ "an bg bs ca cs cy da de el en en-us es et fi fr fr-be ga hr hu is it lt lv mk nl no pl pt-pt ro ru sk sq sr sv ";
+
+ strncpy0(voicename, vname, sizeof(voicename));
if(control & 0x10)
{
strcpy(buf,vname);
@@ -555,21 +625,36 @@ voice_t *LoadVoice(const char *vname, int control)
}
else
{
+ if(voicename[0]==0)
+ strcpy(voicename,"default");
+
sprintf(path_voices,"%s%cvoices%c",path_home,PATHSEP,PATHSEP);
- sprintf(buf,"%s%s",path_voices,voicename);
+ sprintf(buf,"%s%s",path_voices,voicename); // first, look in the main voices directory
if(GetFileLength(buf) <= 0)
{
- // look for the voice in a sub-directory of the language name
- langname[0] = voicename[0];
- langname[1] = voicename[1];
- langname[2] = 0;
- sprintf(buf,"%s%s%c%s",path_voices,langname,PATHSEP,voicename);
-
- if(GetFileLength(buf) <= 0)
+ // then look in the appropriate subdirectory
+ if((voicename[0]=='m') && (voicename[1]=='b'))
+ {
+ voice_dir = "mb"; // mbrola voices
+ }
+ else
{
- // look in "test" sub-directory
- sprintf(buf,"%stest%c%s",path_voices,PATHSEP,voicename);
+ sprintf(name2, "%s ", voicename);
+ if(strstr(voices_europe, voicename) != NULL)
+ voice_dir = "europe";
+ else if(strstr(voices_asia, voicename) != NULL)
+ voice_dir = "asia";
+ else
+ voice_dir = "other";
+
+ sprintf(buf,"%s%s%c%s", path_voices,voice_dir,PATHSEP,voicename);
+
+ if(GetFileLength(buf) <= 0)
+ {
+ // if not found, look in "test" sub-directory
+ sprintf(buf,"%stest%c%s",path_voices,PATHSEP,voicename);
+ }
}
}
}
@@ -604,9 +689,9 @@ voice_t *LoadVoice(const char *vname, int control)
voice_name[0] = 0;
voice_languages[0] = 0;
- voice_selected.identifier = voice_identifier;
- voice_selected.name = voice_name;
- voice_selected.languages = voice_languages;
+ current_voice_selected.identifier = voice_identifier;
+ current_voice_selected.name = voice_name;
+ current_voice_selected.languages = voice_languages;
}
else
{
@@ -631,61 +716,54 @@ voice_t *LoadVoice(const char *vname, int control)
if(buf[0] == 0) continue;
- key = 0;
- for(k=keyword_tab; k->mnem != NULL; k++)
- {
- if(strcmp(buf,k->mnem)==0)
- {
- key = k->data;
- break;
- }
- }
+ key = LookupMnem(keyword_tab, buf);
switch(key)
{
case V_LANGUAGE:
+ {
+ unsigned int len;
+ int priority;
+
+ if(tone_only)
+ break;
+
+ priority = DEFAULT_LANGUAGE_PRIORITY;
+ language_name[0] = 0;
+
+ sscanf(p,"%s %d",language_name,&priority);
+ if(strcmp(language_name,"variant") == 0)
+ break;
+
+ len = strlen(language_name) + 2;
+ // check for space in languages[]
+ if(len < (sizeof(voice_languages)-langix-1))
{
- unsigned int len;
- int priority;
+ voice_languages[langix] = priority;
- if(tone_only)
- break;
-
- priority = DEFAULT_LANGUAGE_PRIORITY;
- language_name[0] = 0;
-
- sscanf(p,"%s %d",language_name,&priority);
- if(strcmp(language_name,"variant") == 0)
- break;
-
- len = strlen(language_name) + 2;
- // check for space in languages[]
- if(len < (sizeof(voice_languages)-langix-1))
- {
- voice_languages[langix] = priority;
-
- strcpy(&voice_languages[langix+1],language_name);
- langix += len;
- }
-
- // only act on the first language line
- if(language_set == 0)
- {
- language_type = strtok(language_name,"-");
- language_set = 1;
- strcpy(translator_name,language_type);
- strcpy(new_dictionary,language_type);
- strcpy(phonemes_name,language_type);
- SelectPhonemeTableName(phonemes_name);
-
- if(new_translator != NULL)
- DeleteTranslator(new_translator);
-
- new_translator = SelectTranslator(translator_name);
- langopts = &new_translator->langopts;
- }
+ strcpy(&voice_languages[langix+1],language_name);
+ langix += len;
}
- break;
+
+ // only act on the first language line
+ if(language_set == 0)
+ {
+ language_type = strtok(language_name,"-");
+ language_set = 1;
+ strcpy(translator_name,language_type);
+ strcpy(new_dictionary,language_type);
+ strcpy(phonemes_name,language_type);
+ SelectPhonemeTableName(phonemes_name);
+
+ if(new_translator != NULL)
+ DeleteTranslator(new_translator);
+
+ new_translator = SelectTranslator(translator_name);
+ langopts = &new_translator->langopts;
+ strncpy0(voice->language_name, language_name, sizeof(voice->language_name));
+ }
+ }
+ break;
case V_NAME:
if(tone_only == 0)
@@ -696,14 +774,14 @@ voice_t *LoadVoice(const char *vname, int control)
break;
case V_GENDER:
- {
- int age;
- char vgender[80];
- sscanf(p,"%s %d",vgender,&age);
- voice_selected.gender = LookupMnem(genders,vgender);
- voice_selected.age = age;
- }
- break;
+ {
+ int age = 0;
+ char vgender[80];
+ sscanf(p,"%s %d",vgender,&age);
+ current_voice_selected.gender = LookupMnem(genders,vgender);
+ current_voice_selected.age = age;
+ }
+ break;
case V_TRANSLATOR:
if(tone_only) break;
@@ -711,7 +789,7 @@ voice_t *LoadVoice(const char *vname, int control)
sscanf(p,"%s",translator_name);
if(new_translator != NULL)
- DeleteTranslator(new_translator);
+ DeleteTranslator(new_translator);
new_translator = SelectTranslator(translator_name);
langopts = &new_translator->langopts;
@@ -730,16 +808,16 @@ voice_t *LoadVoice(const char *vname, int control)
break;
case V_PITCH:
- {
- double factor;
- // default is pitch 82 118
- n = sscanf(p,"%d %d",&pitch1,&pitch2);
- voice->pitch_base = (pitch1 - 9) << 12;
- voice->pitch_range = (pitch2 - pitch1) * 108;
- factor = double(pitch1 - 82)/82;
- voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch
- }
- break;
+ {
+ double factor;
+ // default is pitch 82 118
+ n = sscanf(p,"%d %d",&pitch1,&pitch2);
+ voice->pitch_base = (pitch1 - 9) << 12;
+ voice->pitch_range = (pitch2 - pitch1) * 108;
+ factor = (double)(pitch1 - 82)/82;
+ voice->formant_factor = (int)((1+factor/4) * 256); // nominal formant shift for a different voice pitch
+ }
+ break;
case V_STRESSLENGTH: // stressLength
stress_lengths_set = Read8Numbers(p,stress_lengths);
@@ -759,18 +837,52 @@ voice_t *LoadVoice(const char *vname, int control)
langopts->intonation_group = option_tone_flags & 0xff;
break;
+ case V_TUNES:
+ n = sscanf(p,"%s %s %s %s %s %s",names[0],names[1],names[2],names[3],names[4],names[5]);
+ langopts->intonation_group = 0;
+ for(ix=0; ix<n; ix++)
+ {
+ if(strcmp(names[ix],"NULL")==0)
+ continue;
+
+ if((value = LookupTune(names[ix])) < 0)
+ fprintf(stderr,"Unknown tune '%s'\n",names[ix]);
+ else
+ langopts->tunes[ix] = value;
+ }
+ break;
+
case V_DICTRULES: // conditional dictionary rules and list entries
+ case V_NUMBERS:
+ case V_STRESSOPT:
+ // expect a list of numbers
while(*p != 0)
{
while(isspace(*p)) p++;
n = -1;
- if(((n = atoi(p)) > 0) && (n < 32))
+ if((n = atoi(p)) > 0)
{
p++;
- conditional_rules |= (1 << n);
+ if(n < 32)
+ {
+ if(key==V_DICTRULES)
+ conditional_rules |= (1 << n);
+ else if(key==V_NUMBERS)
+ langopts->numbers |= (1 << n);
+ else if(key==V_STRESSOPT)
+ langopts->stress_flags |= (1 << n);
+ }
+ else
+ {
+ if((key==V_NUMBERS) && (n < 64))
+ langopts->numbers |= (1 << (n-32));
+ else
+ fprintf(stderr,"Bad option number %d\n", n);
+ }
}
while(isalnum(*p)) p++;
}
+ ProcessLanguageOptions(langopts);
break;
case V_REPLACE:
@@ -789,9 +901,9 @@ voice_t *LoadVoice(const char *vname, int control)
case V_STRESSRULE:
sscanf(p,"%d %d %d %d",&langopts->stress_rule,
- &langopts->stress_flags,
- &langopts->unstressed_wd1,
- &langopts->unstressed_wd2);
+ &langopts->stress_flags,
+ &langopts->unstressed_wd1,
+ &langopts->unstressed_wd2);
break;
case V_CHARSET:
@@ -799,15 +911,17 @@ voice_t *LoadVoice(const char *vname, int control)
new_translator->charset_a0 = charsets[value];
break;
- case V_NUMBERS:
- sscanf(p,"%d %d",&langopts->numbers,&langopts->numbers2);
- break;
-
case V_OPTION:
- if(sscanf(p,"%d %d",&ix,&value) == 2)
+ value2 = 0;
+ if(((sscanf(p,"%s %d %d",option_name,&value,&value2) >= 2) && ((ix = LookupMnem(options_tab, option_name)) >= 0)) ||
+ ((sscanf(p,"%d %d %d",&ix,&value,&value2) >= 2) && (ix < N_LOPTS)))
+ {
+ langopts->param[ix] = value;
+ langopts->param2[ix] = value2;
+ }
+ else
{
- if((ix >= 0) && (ix < N_LOPTS))
- langopts->param[ix] = value;
+ fprintf(stderr,"Bad voice option: %s %s\n",buf,p);
}
break;
@@ -841,12 +955,12 @@ voice_t *LoadVoice(const char *vname, int control)
break;
case V_TONE:
- {
- int tone_data[12];
- ReadTonePoints(p,tone_data);
- SetToneAdjust(voice,tone_data);
- }
- break;
+ {
+ int tone_data[12];
+ ReadTonePoints(p,tone_data);
+ SetToneAdjust(voice,tone_data);
+ }
+ break;
case V_VOICING:
if(sscanf(p,"%d",&value)==1)
@@ -854,33 +968,39 @@ voice_t *LoadVoice(const char *vname, int control)
break;
case V_BREATH:
- voice->breath[0] = Read8Numbers(p,&voice->breath[1]);
- for(ix=1; ix<8; ix++)
- {
- if(ix % 2)
- voice->breath[ix] = -voice->breath[ix];
- }
+ voice->breath[0] = Read8Numbers(p,&voice->breath[1]);
+ for(ix=1; ix<8; ix++)
+ {
+ if(ix % 2)
+ voice->breath[ix] = -voice->breath[ix];
+ }
break;
case V_BREATHW:
- voice->breathw[0] = Read8Numbers(p,&voice->breathw[1]);
+ voice->breathw[0] = Read8Numbers(p,&voice->breathw[1]);
break;
case V_CONSONANTS:
value = sscanf(p,"%d %d",&voice->consonant_amp, &voice->consonant_ampv);
break;
+ case V_SPEED:
+ sscanf(p,"%d",&voice->speed_percent);
+ break;
+
case V_MBROLA:
- {
- int srate = 16000;
- char name[40];
- char phtrans[40];
+ {
+ int srate = 16000;
- phtrans[0] = 0;
- sscanf(p,"%s %s %d",name,phtrans,&srate);
- LoadMbrolaTable(name,phtrans,srate);
+ name2[0] = 0;
+ sscanf(p,"%s %s %d",name1,name2,&srate);
+ if(LoadMbrolaTable(name1,name2,srate) != EE_OK)
+ {
+ fprintf(stderr,"mbrola voice not found\n");
}
- break;
+ voice->samplerate = srate;
+ }
+ break;
case V_KLATT:
voice->klattv[0] = 1; // default source: IMPULSIVE
@@ -890,7 +1010,48 @@ voice_t *LoadVoice(const char *vname, int control)
case V_FAST:
Read8Numbers(p,speed.fast_settings);
- SetSpeed(2);
+ SetSpeed(3);
+ break;
+
+ case V_DICTMIN:
+ sscanf(p,"%d",&dict_min);
+ break;
+
+ case V_ALPHABET2:
+ {
+ ALPHABET *alphabet;
+ name1[0] = name2[0] = 0;
+ sscanf(p, "%s %s", name1, name2);
+
+ if(strcmp(name1, "latin") == 0)
+ {
+ strncpy0(langopts->ascii_language,name2,sizeof(langopts->ascii_language));
+ }
+ else if((alphabet = AlphabetFromName(name1)) != 0)
+ {
+ langopts->alt_alphabet = alphabet->offset;
+ langopts->alt_alphabet_lang = StringToWord2(name2);
+ }
+ else
+ {
+ fprintf(stderr,"alphabet name '%s' not found\n", name1);
+ }
+ }
+ break;
+
+ case V_DICTDIALECT:
+ // specify a dialect to use for foreign words, eg, en-us for _^_EN
+ if(sscanf(p, "%s", name1) == 1)
+ {
+ if((ix = LookupMnem(dict_dialects, name1)) > 0)
+ {
+ langopts->dict_dialect |= (1 << ix);
+ }
+ else
+ {
+ fprintf(stderr, "dictdialect name '%s' not recognized\n", name1);
+ }
+ }
break;
default:
@@ -914,6 +1075,8 @@ voice_t *LoadVoice(const char *vname, int control)
new_translator = SelectTranslator(translator_name);
}
+ SetSpeed(3); // for speed_percent
+
for(ix=0; ix<N_PEAKS; ix++)
{
voice->freq2[ix] = voice->freq[ix];
@@ -930,9 +1093,12 @@ voice_t *LoadVoice(const char *vname, int control)
if((ix = SelectPhonemeTableName(phonemes_name)) < 0)
{
fprintf(stderr,"Unknown phoneme table: '%s'\n",phonemes_name);
+ ix = 0;
}
voice->phoneme_tab_ix = ix;
- error = LoadDictionary(new_translator, new_dictionary, control & 4);
+ new_translator->phoneme_tab_ix = ix;
+ new_translator->dict_min_size = dict_min;
+ LoadDictionary(new_translator, new_dictionary, control & 4);
if(dictionary_name[0]==0)
return(NULL); // no dictionary loaded
@@ -956,6 +1122,7 @@ voice_t *LoadVoice(const char *vname, int control)
translator = new_translator;
}
+
// relative lengths of different stress syllables
for(ix=0; ix<stress_lengths_set; ix++)
{
@@ -975,37 +1142,39 @@ voice_t *LoadVoice(const char *vname, int control)
} // end of LoadVoice
-static char *ExtractVoiceVariantName(char *vname, int variant_num)
-{//===============================================================
+static char *ExtractVoiceVariantName(char *vname, int variant_num, int add_dir)
+{//===========================================================================
// Remove any voice variant suffix (name or number) from a voice name
// Returns the voice variant name
char *p;
- static char variant_name[20];
+ static char variant_name[40];
char variant_prefix[5];
variant_name[0] = 0;
sprintf(variant_prefix,"!v%c",PATHSEP);
+ if(add_dir == 0)
+ variant_prefix[0] = 0;
if(vname != NULL)
{
if((p = strchr(vname,'+')) != NULL)
{
// The voice name has a +variant suffix
+ variant_num = 0;
*p++ = 0; // delete the suffix from the voice name
- if(isdigit(*p))
+ if(IsDigit09(*p))
{
variant_num = atoi(p); // variant number
}
else
{
// voice variant name, not number
- strcpy(variant_name,variant_prefix);
- strncpy0(&variant_name[3],p,sizeof(variant_name)-3);
- }
+ sprintf(variant_name, "%s%s", variant_prefix, p);
+ }
}
}
-
+
if(variant_num > 0)
{
if(variant_num < 10)
@@ -1029,7 +1198,7 @@ voice_t *LoadVoiceVariant(const char *vname, int variant_num)
char buf[60];
strncpy0(buf,vname,sizeof(buf));
- variant_name = ExtractVoiceVariantName(buf,variant_num);
+ variant_name = ExtractVoiceVariantName(buf,variant_num, 1);
if((v = LoadVoice(buf,0)) == NULL)
return(NULL);
@@ -1087,10 +1256,10 @@ static int ScoreVoice(espeak_VOICE *voice_spec, const char *spec_language, int s
p = voice->languages; // list of languages+dialects for which this voice is suitable
- if(strcmp(spec_language,"mbrola")==0)
+ if(spec_n_parts < 0)
{
- // only list mbrola voices
- if(memcmp(voice->identifier,"mb/",3) == 0)
+ // match on the subdirectory
+ if(memcmp(voice->identifier, spec_language, spec_lang_len) == 0)
return(100);
return(0);
}
@@ -1168,15 +1337,14 @@ static int ScoreVoice(espeak_VOICE *voice_spec, const char *spec_language, int s
// match on voice name
score += 500;
}
- else
- if(strcmp(voice_spec->name,voice->identifier)==0)
+ else if(strcmp(voice_spec->name,voice->identifier)==0)
{
score += 400;
}
}
if(((voice_spec->gender == 1) || (voice_spec->gender == 2)) &&
- ((voice->gender == 1) || (voice->gender == 2)))
+ ((voice->gender == 1) || (voice->gender == 2)))
{
if(voice_spec->gender == voice->gender)
score += 50;
@@ -1224,6 +1392,7 @@ static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int
int lang_len=0;
espeak_VOICE *vp;
char language[80];
+ char buf[sizeof(path_home)+80];
// count number of parts in the specified language
if((voice_select->languages != NULL) && (voice_select->languages[0] != 0))
@@ -1236,6 +1405,26 @@ static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int
n_parts++;
}
}
+
+ if((n_parts == 1) && (control & 1))
+ {
+ if(strcmp(language, "mbrola") == 0)
+ {
+ language[2] = 0; // truncate to "mb"
+ lang_len = 2;
+ }
+
+ sprintf(buf, "%s/voices/%s", path_home, language);
+ if(GetFileLength(buf) == -2)
+ {
+ // A subdirectory name has been specified. List all the voices in that subdirectory
+ language[lang_len++] = PATHSEP;
+ language[lang_len] = 0;
+ n_parts = -1;
+ }
+
+ }
+
// select those voices which match the specified language
nv = 0;
for(ix=0; ix<n_voices_list; ix++)
@@ -1257,7 +1446,7 @@ static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int
return(0);
// sort the selected voices by their score
- qsort(voices,nv,sizeof(espeak_VOICE *),VoiceScoreSorter);
+ qsort(voices,nv,sizeof(espeak_VOICE *),(int (__cdecl *)(const void *,const void *))VoiceScoreSorter);
return(nv);
} // end of SetVoiceScores
@@ -1265,15 +1454,17 @@ static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int
-espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name)
-{//=====================================================================
+espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name2)
+{//======================================================================
int ix;
int match_fname = -1;
int match_fname2 = -1;
int match_name = -1;
- const char *id;
+ const char *id; // this is the filename within espeak-data/voices
+ char *variant_name;
int last_part_len;
char last_part[41];
+ char name[40];
if(voices == NULL)
{
@@ -1282,6 +1473,13 @@ espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name)
voices = voices_list;
}
+ strncpy0(name, name2, sizeof(name));
+ if((variant_name = strchr(name, '+')) != NULL)
+ {
+ *variant_name = 0;
+ variant_name++;
+ }
+
sprintf(last_part,"%c%s",PATHSEP,name);
last_part_len = strlen(last_part);
@@ -1293,14 +1491,16 @@ espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name)
break;
}
else
- if(strcmp(name,id = voices[ix]->identifier)==0)
{
- match_fname = ix; // matching identifier, use this if no matching name
- }
- else
- if(strcmp(last_part,&id[strlen(id)-last_part_len])==0)
- {
- match_fname2 = ix;
+ id = voices[ix]->identifier;
+ if(strcmp(name, id)==0)
+ {
+ match_fname = ix; // matching identifier, use this if no matching name
+ }
+ else if(strcmp(last_part,&id[strlen(id)-last_part_len])==0)
+ {
+ match_fname2 = ix;
+ }
}
}
@@ -1352,15 +1552,15 @@ char const *SelectVoice(espeak_VOICE *voice_select, int *found)
{
// no language is specified. Get language from the named voice
static char buf[60];
-
+
if(voice_select2.name == NULL)
{
if((voice_select2.name = voice_select2.identifier) == NULL)
voice_select2.name = "default";
}
-
+
strncpy0(buf,voice_select2.name,sizeof(buf));
- variant_name = ExtractVoiceVariantName(buf,0);
+ variant_name = ExtractVoiceVariantName(buf,0,0);
vp = SelectVoiceByName(voices_list,buf);
if(vp != NULL)
@@ -1371,7 +1571,7 @@ char const *SelectVoice(espeak_VOICE *voice_select, int *found)
{
if(variant_name[0] != 0)
{
- sprintf(voice_id,"%s+%s",vp->identifier,&variant_name[3]); // omit the !v/ from variant_name
+ sprintf(voice_id,"%s+%s", vp->identifier, variant_name);
return(voice_id);
}
@@ -1394,8 +1594,7 @@ char const *SelectVoice(espeak_VOICE *voice_select, int *found)
gender = 0;
if((voice_select2.gender == 2) || ((voice_select2.age > 0) && (voice_select2.age < 13)))
gender = 2;
- else
- if(voice_select2.gender == 1)
+ else if(voice_select2.gender == 1)
gender = 1;
#define AGE_OLD 60
@@ -1413,6 +1612,7 @@ char const *SelectVoice(espeak_VOICE *voice_select, int *found)
vp = voices[ix];
// is the main voice the required gender?
skip=0;
+
if((gender != 0) && (vp->gender != gender))
{
skip=1;
@@ -1421,9 +1621,10 @@ char const *SelectVoice(espeak_VOICE *voice_select, int *found)
{
skip=1;
}
+
if(skip==0)
{
- voices2[ix2++] = vp;
+ voices2[ix2++] = vp;
}
for(j=0; (j < vp->xx1) && (n_variants < N_VOICE_VARIANTS);)
@@ -1452,12 +1653,14 @@ char const *SelectVoice(espeak_VOICE *voice_select, int *found)
}
// index the sorted list by the required variant number
+ if(ix2 == 0)
+ return(NULL);
vp = voices2[voice_select2.variant % ix2];
if(vp->variant != 0)
{
- variant_name = ExtractVoiceVariantName(NULL,vp->variant);
- sprintf(voice_id,"%s+%s",vp->identifier,&variant_name[3]);
+ variant_name = ExtractVoiceVariantName(NULL, vp->variant, 0);
+ sprintf(voice_id,"%s+%s", vp->identifier, variant_name);
return(voice_id);
}
@@ -1508,10 +1711,10 @@ static void GetVoices(const char *path)
}
else
{
- // a regular line, add it to the voices list
+ // a regular line, add it to the voices list
if((f_voice = fopen(fname,"r")) == NULL)
continue;
-
+
// pass voice file name within the voices directory
voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, &buf[20]);
fclose(f_voice);
@@ -1524,8 +1727,8 @@ static void GetVoices(const char *path)
}
#else
#ifdef PLATFORM_WINDOWS
- WIN32_FIND_DATAA FindFileData;
- HANDLE hFind = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATAA FindFileData;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
#undef UNICODE // we need FindFirstFileA() which takes an 8-bit c-string
sprintf(fname,"%s\\*",path);
@@ -1534,29 +1737,33 @@ static void GetVoices(const char *path)
return;
do {
- sprintf(fname,"%s%c%s",path,PATHSEP,FindFileData.cFileName);
-
- ftype = GetFileLength(fname);
+ if(n_voices_list >= (N_VOICES_LIST-2))
+ break; // voices list is full
- if((ftype == -2) && (FindFileData.cFileName[0] != '.'))
+ if(FindFileData.cFileName[0] != '.')
{
- // a sub-sirectory
- GetVoices(fname);
- }
- else
- if(ftype > 0)
- {
- // a regular line, add it to the voices list
- if((f_voice = fopen(fname,"r")) == NULL)
- continue;
-
- // pass voice file name within the voices directory
- voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, FindFileData.cFileName);
- fclose(f_voice);
+ sprintf(fname,"%s%c%s",path,PATHSEP,FindFileData.cFileName);
+ ftype = GetFileLength(fname);
- if(voice_data != NULL)
+ if(ftype == -2)
{
- voices_list[n_voices_list++] = voice_data;
+ // a sub-sirectory
+ GetVoices(fname);
+ }
+ else if(ftype > 0)
+ {
+ // a regular line, add it to the voices list
+ if((f_voice = fopen(fname,"r")) == NULL)
+ continue;
+
+ // pass voice file name within the voices directory
+ voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, FindFileData.cFileName);
+ fclose(f_voice);
+
+ if(voice_data != NULL)
+ {
+ voices_list[n_voices_list++] = voice_data;
+ }
}
}
} while(FindNextFileA(hFind, &FindFileData) != 0);
@@ -1574,22 +1781,24 @@ static void GetVoices(const char *path)
if(n_voices_list >= (N_VOICES_LIST-2))
break; // voices list is full
+ if(ent->d_name[0] == '.')
+ continue;
+
sprintf(fname,"%s%c%s",path,PATHSEP,ent->d_name);
ftype = GetFileLength(fname);
- if((ftype == -2) && (ent->d_name[0] != '.'))
+ if(ftype == -2)
{
// a sub-sirectory
GetVoices(fname);
}
- else
- if(ftype > 0)
+ else if(ftype > 0)
{
- // a regular line, add it to the voices list
+ // a regular line, add it to the voices list
if((f_voice = fopen(fname,"r")) == NULL)
continue;
-
+
// pass voice file name within the voices directory
voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, ent->d_name);
fclose(f_voice);
@@ -1610,15 +1819,23 @@ static void GetVoices(const char *path)
espeak_ERROR SetVoiceByName(const char *name)
{//=========================================
espeak_VOICE *v;
+ int ix;
espeak_VOICE voice_selector;
char *variant_name;
static char buf[60];
strncpy0(buf,name,sizeof(buf));
- variant_name = ExtractVoiceVariantName(buf,0);
+
+ variant_name = ExtractVoiceVariantName(buf, 0, 1);
+
+ for(ix=0; ; ix++)
+ {
+ // convert voice name to lower case (ascii)
+ if((buf[ix] = tolower(buf[ix])) == 0)
+ break;
+ }
memset(&voice_selector,0,sizeof(voice_selector));
-// voice_selector.name = buf;
voice_selector.name = (char *)name; // include variant name in voice stack ??
// first check for a voice with this filename
@@ -1632,7 +1849,8 @@ espeak_ERROR SetVoiceByName(const char *name)
}
DoVoiceChange(voice);
- SetVoiceStack(&voice_selector);
+ voice_selector.languages = voice->language_name;
+ SetVoiceStack(&voice_selector, variant_name);
return(EE_OK);
}
@@ -1648,7 +1866,8 @@ espeak_ERROR SetVoiceByName(const char *name)
LoadVoice(variant_name,2);
}
DoVoiceChange(voice);
- SetVoiceStack(&voice_selector);
+ voice_selector.languages = voice->language_name;
+ SetVoiceStack(&voice_selector, variant_name);
return(EE_OK);
}
}
@@ -1669,12 +1888,25 @@ espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector)
LoadVoiceVariant(voice_id,0);
DoVoiceChange(voice);
- SetVoiceStack(voice_selector);
+ SetVoiceStack(voice_selector, "");
return(EE_OK);
} // end of SetVoiceByProperties
+void FreeVoiceList(void)
+{//=================
+ int ix;
+ for(ix=0; ix<n_voices_list; ix++)
+ {
+ if(voices_list[ix] != NULL)
+ {
+ free(voices_list[ix]);
+ voices_list[ix] = NULL;
+ }
+ }
+ n_voices_list = 0;
+}
//=======================================================================
@@ -1683,32 +1915,40 @@ espeak_ERROR SetVoiceByProperties(espeak_VOICE *voice_selector)
#pragma GCC visibility push(default)
-const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec)
+ESPEAK_API const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec)
{//========================================================================
-#ifndef PLATFORM_RISCOS
- int ix;
- int j;
- espeak_VOICE *v;
- static espeak_VOICE *voices[N_VOICES_LIST];
char path_voices[sizeof(path_home)+12];
- // free previous voice list data
- for(ix=0; ix<n_voices_list; ix++)
+#ifdef PLATFORM_RISCOS
+ if(n_voices_list == 0)
{
- if(voices_list[ix] != NULL)
- free(voices_list[ix]);
+ sprintf(path_voices,"%s%cvoices",path_home,PATHSEP);
+ len_path_voices = strlen(path_voices)+1;
+ GetVoices(path_voices);
+ voices_list[n_voices_list] = NULL; // voices list terminator
}
- n_voices_list = 0;
+ return((const espeak_VOICE **)voices_list);
+
+#else
+ int ix;
+ int j;
+ espeak_VOICE *v;
+ static espeak_VOICE **voices = NULL;
+
+ // free previous voice list data
+ FreeVoiceList();
sprintf(path_voices,"%s%cvoices",path_home,PATHSEP);
len_path_voices = strlen(path_voices)+1;
GetVoices(path_voices);
voices_list[n_voices_list] = NULL; // voices list terminator
+ voices = (espeak_VOICE **)realloc(voices, sizeof(espeak_VOICE *)*(n_voices_list+1));
// sort the voices list
- qsort(voices_list,n_voices_list,sizeof(espeak_VOICE *),VoiceNameSorter);
+ qsort(voices_list,n_voices_list,sizeof(espeak_VOICE *),
+ (int (__cdecl *)(const void *,const void *))VoiceNameSorter);
if(voice_spec)
@@ -1718,11 +1958,12 @@ const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec)
}
else
{
- // list all: omit variant voices and mbrola voices
+ // list all: omit variant voices and mbrola voices and test voices
j = 0;
for(ix=0; (v = voices_list[ix]) != NULL; ix++)
{
- if((v->languages[0] != 0) && (strcmp(&v->languages[1],"variant") != 0) && (memcmp(v->identifier,"mb/",3) != 0))
+ if((v->languages[0] != 0) && (strcmp(&v->languages[1],"variant") != 0)
+ && (memcmp(v->identifier,"mb/",3) != 0) && (memcmp(v->identifier,"test/",5) != 0))
{
voices[j++] = v;
}
@@ -1731,14 +1972,13 @@ const espeak_VOICE **espeak_ListVoices(espeak_VOICE *voice_spec)
}
return((const espeak_VOICE **)voices);
#endif
- return((const espeak_VOICE **)voices_list);
} // end of espeak_ListVoices
-espeak_VOICE *espeak_GetCurrentVoice(void)
+ESPEAK_API espeak_VOICE *espeak_GetCurrentVoice(void)
{//==================================================
- return(&voice_selected);
+ return(&current_voice_selected);
}
#pragma GCC visibility pop
diff --git a/navit/support/espeak/voices.h b/navit/support/espeak/voices.h
new file mode 100644
index 000000000..920de021a
--- /dev/null
+++ b/navit/support/espeak/voices.h
@@ -0,0 +1,19 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2010 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+void FreeVoiceList(void);
diff --git a/navit/support/espeak/wave.c b/navit/support/espeak/wave.c
index 364dcf577..591283665 100755..100644
--- a/navit/support/espeak/wave.c
+++ b/navit/support/espeak/wave.c
@@ -32,7 +32,9 @@
#include <time.h>
#include "portaudio.h"
-#ifndef PLATFORM_WINDOWS
+#ifdef PLATFORM_WINDOWS
+#include <windows.h>
+#else
#include <unistd.h>
#endif
#include "wave.h"
@@ -40,6 +42,15 @@
//<Definitions
+#ifdef NEED_STRUCT_TIMESPEC
+#define HAVE_STRUCT_TIMESPEC 1
+struct timespec {
+ long tv_sec;
+ long tv_nsec;
+};
+#endif /* HAVE_STRUCT_TIMESPEC */
+
+
enum {ONE_BILLION=1000000000};
#ifdef USE_PORTAUDIO
@@ -53,18 +64,161 @@ enum {ONE_BILLION=1000000000};
#endif
+
+
+#ifdef USE_PULSEAUDIO
+// create some wrappers for runtime detection
+
+// checked on wave_init
+static int pulse_running;
+
+// wave.cpp (this file)
+int wave_port_init(int);
+void* wave_port_open(const char* the_api);
+size_t wave_port_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize);
+int wave_port_close(void* theHandler);
+int wave_port_is_busy(void* theHandler);
+void wave_port_terminate();
+uint32_t wave_port_get_read_position(void* theHandler);
+uint32_t wave_port_get_write_position(void* theHandler);
+void wave_port_flush(void* theHandler);
+void wave_port_set_callback_is_output_enabled(t_wave_callback* cb);
+void* wave_port_test_get_write_buffer();
+int wave_port_get_remaining_time(uint32_t sample, uint32_t* time);
+
+// wave_pulse.cpp
+int is_pulse_running();
+int wave_pulse_init(int);
+void* wave_pulse_open(const char* the_api);
+size_t wave_pulse_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize);
+int wave_pulse_close(void* theHandler);
+int wave_pulse_is_busy(void* theHandler);
+void wave_pulse_terminate();
+uint32_t wave_pulse_get_read_position(void* theHandler);
+uint32_t wave_pulse_get_write_position(void* theHandler);
+void wave_pulse_flush(void* theHandler);
+void wave_pulse_set_callback_is_output_enabled(t_wave_callback* cb);
+void* wave_pulse_test_get_write_buffer();
+int wave_pulse_get_remaining_time(uint32_t sample, uint32_t* time);
+
+// wrappers
+int wave_init(int srate) {
+ pulse_running = is_pulse_running();
+
+ if (pulse_running)
+ return wave_pulse_init(srate);
+ else
+ return wave_port_init(srate);
+}
+
+void* wave_open(const char* the_api) {
+ if (pulse_running)
+ return wave_pulse_open(the_api);
+ else
+ return wave_port_open(the_api);
+}
+
+size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {
+ if (pulse_running)
+ return wave_pulse_write(theHandler, theMono16BitsWaveBuffer, theSize);
+ else
+ return wave_port_write(theHandler, theMono16BitsWaveBuffer, theSize);
+}
+
+int wave_close(void* theHandler) {
+ if (pulse_running)
+ return wave_pulse_close(theHandler);
+ else
+ return wave_port_close(theHandler);
+}
+
+int wave_is_busy(void* theHandler) {
+ if (pulse_running)
+ return wave_pulse_is_busy(theHandler);
+ else
+ return wave_port_is_busy(theHandler);
+}
+
+void wave_terminate() {
+ if (pulse_running)
+ wave_pulse_terminate();
+ else
+ wave_port_terminate();
+}
+
+uint32_t wave_get_read_position(void* theHandler) {
+ if (pulse_running)
+ return wave_pulse_get_read_position(theHandler);
+ else
+ return wave_port_get_read_position(theHandler);
+}
+
+uint32_t wave_get_write_position(void* theHandler) {
+ if (pulse_running)
+ return wave_pulse_get_write_position(theHandler);
+ else
+ return wave_port_get_write_position(theHandler);
+}
+
+void wave_flush(void* theHandler) {
+ if (pulse_running)
+ wave_pulse_flush(theHandler);
+ else
+ wave_port_flush(theHandler);
+}
+
+void wave_set_callback_is_output_enabled(t_wave_callback* cb) {
+ if (pulse_running)
+ wave_pulse_set_callback_is_output_enabled(cb);
+ else
+ wave_port_set_callback_is_output_enabled(cb);
+}
+
+void* wave_test_get_write_buffer() {
+ if (pulse_running)
+ return wave_pulse_test_get_write_buffer();
+ else
+ return wave_port_test_get_write_buffer();
+}
+
+int wave_get_remaining_time(uint32_t sample, uint32_t* time)
+{
+ if (pulse_running)
+ return wave_pulse_get_remaining_time(sample, time);
+ else
+ return wave_port_get_remaining_time(sample, time);
+}
+
+// rename functions to be wrapped
+#define wave_init wave_port_init
+#define wave_open wave_port_open
+#define wave_write wave_port_write
+#define wave_close wave_port_close
+#define wave_is_busy wave_port_is_busy
+#define wave_terminate wave_port_terminate
+#define wave_get_read_position wave_port_get_read_position
+#define wave_get_write_position wave_port_get_write_position
+#define wave_flush wave_port_flush
+#define wave_set_callback_is_output_enabled wave_port_set_callback_is_output_enabled
+#define wave_test_get_write_buffer wave_port_test_get_write_buffer
+#define wave_get_remaining_time wave_port_get_remaining_time
+
+#endif // USE_PULSEAUDIO
+
+
static t_wave_callback* my_callback_is_output_enabled=NULL;
#define N_WAV_BUF 10
-#define SAMPLE_RATE 22050
+#define MAX_SAMPLE_RATE 22050
#define FRAMES_PER_BUFFER 512
-#define BUFFER_LENGTH (SAMPLE_RATE*2*sizeof(uint16_t))
-#define THRESHOLD (BUFFER_LENGTH/5)
+#define BUFFER_LENGTH (MAX_SAMPLE_RATE*2*sizeof(uint16_t))
+//#define THRESHOLD (BUFFER_LENGTH/5)
static char myBuffer[BUFFER_LENGTH];
-static char* myRead=NULL;
-static char* myWrite=NULL;
+static char* myRead=NULL;
+static char* myWrite=NULL;
static int out_channels=1;
static int my_stream_could_start=0;
+static int wave_samplerate;
static int mInCallbackFinishedState = false;
#if (USE_PORTAUDIO == 18)
@@ -80,7 +234,7 @@ static PaError pa_init_err=0;
// time measurement
// The read and write position audio stream in the audio stream are measured in ms.
-//
+//
// * When the stream is opened, myReadPosition and myWritePosition are cleared.
// * myWritePosition is updated in wave_write.
// * myReadPosition is updated in pa_callback (+ sample delay).
@@ -132,12 +286,12 @@ static void start_stream()
PaError err;
SHOW_TIME("start_stream");
- my_stream_could_start=0;
+ my_stream_could_start=0;
mInCallbackFinishedState = false;
err = Pa_StartStream(pa_stream);
SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err));
-
+
#if USE_PORTAUDIO == 19
if(err == paStreamIsNotStopped)
{
@@ -168,89 +322,89 @@ static int pa_callback(void *inputBuffer, void *outputBuffer,
PaStreamCallbackFlags flags, void *userData )
#endif
{
- int aResult=0; // paContinue
- char* aWrite = myWrite;
- size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer;
+ int aResult=0; // paContinue
+ char* aWrite = myWrite;
+ size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer;
- myReadPosition += framesPerBuffer;
- SHOW("pa_callback > myReadPosition=%u, framesPerBuffer=%lu (n=0x%x) \n",(int)myReadPosition, framesPerBuffer, n);
+ myReadPosition += framesPerBuffer;
+ SHOW("pa_callback > myReadPosition=%u, framesPerBuffer=%lu (n=0x%x) \n",(int)myReadPosition, framesPerBuffer, n);
- if (aWrite >= myRead)
- {
- if((size_t)(aWrite - myRead) >= n)
- {
- memcpy(outputBuffer, myRead, n);
- myRead += n;
- }
- else
+ if (aWrite >= myRead)
{
- SHOW_TIME("pa_callback > underflow");
- aResult=1; // paComplete;
- mInCallbackFinishedState = true;
- size_t aUsedMem=0;
- aUsedMem = (size_t)(aWrite - myRead);
- if (aUsedMem)
- {
- memcpy(outputBuffer, myRead, aUsedMem);
- }
- char* p = (char*)outputBuffer + aUsedMem;
- memset(p, 0, n - aUsedMem);
- // myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
- myRead = aWrite;
- }
- }
- else // myRead > aWrite
- {
- if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n)
- {
- memcpy(outputBuffer, myRead, n);
- myRead += n;
+ if((size_t)(aWrite - myRead) >= n)
+ {
+ memcpy(outputBuffer, myRead, n);
+ myRead += n;
+ }
+ else
+ {
+ SHOW_TIME("pa_callback > underflow");
+ aResult=1; // paComplete;
+ mInCallbackFinishedState = true;
+ size_t aUsedMem=0;
+ aUsedMem = (size_t)(aWrite - myRead);
+ if (aUsedMem)
+ {
+ memcpy(outputBuffer, myRead, aUsedMem);
+ }
+ char* p = (char*)outputBuffer + aUsedMem;
+ memset(p, 0, n - aUsedMem);
+ // myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
+ myRead = aWrite;
+ }
}
- else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n)
+ else // myRead > aWrite
{
- int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
- if (aTopMem)
- {
- SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
- memcpy(outputBuffer, myRead, aTopMem);
- }
- int aRest = n - aTopMem;
- if (aRest)
- {
- SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
- char* p = (char*)outputBuffer + aTopMem;
- memcpy(p, myBuffer, aRest);
- }
- myRead = myBuffer + aRest;
- }
- else
- {
- SHOW_TIME("pa_callback > underflow");
- aResult=1; // paComplete;
-
- int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
- if (aTopMem)
- {
- SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
- memcpy(outputBuffer, myRead, aTopMem);
- }
- int aRest = aWrite - myBuffer;
- if (aRest)
- {
- SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
- char* p = (char*)outputBuffer + aTopMem;
- memcpy(p, myBuffer, aRest);
- }
-
- size_t aUsedMem = aTopMem + aRest;
- char* p = (char*)outputBuffer + aUsedMem;
- memset(p, 0, n - aUsedMem);
- // myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
- myRead = aWrite;
+ if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n)
+ {
+ memcpy(outputBuffer, myRead, n);
+ myRead += n;
+ }
+ else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n)
+ {
+ int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
+ if (aTopMem)
+ {
+ SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
+ memcpy(outputBuffer, myRead, aTopMem);
+ }
+ int aRest = n - aTopMem;
+ if (aRest)
+ {
+ SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
+ char* p = (char*)outputBuffer + aTopMem;
+ memcpy(p, myBuffer, aRest);
+ }
+ myRead = myBuffer + aRest;
+ }
+ else
+ {
+ SHOW_TIME("pa_callback > underflow");
+ aResult=1; // paComplete;
+
+ int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
+ if (aTopMem)
+ {
+ SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
+ memcpy(outputBuffer, myRead, aTopMem);
+ }
+ int aRest = aWrite - myBuffer;
+ if (aRest)
+ {
+ SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
+ char* p = (char*)outputBuffer + aTopMem;
+ memcpy(p, myBuffer, aRest);
+ }
+
+ size_t aUsedMem = aTopMem + aRest;
+ char* p = (char*)outputBuffer + aUsedMem;
+ memset(p, 0, n - aUsedMem);
+ // myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
+ myRead = aWrite;
+ }
}
- }
- SHOW("pa_callback > myRead=%x\n",(int)myRead);
+ SHOW("pa_callback > myRead=%x\n",(int)myRead);
// #if USE_PORTAUDIO == 18
@@ -288,7 +442,7 @@ static int pa_callback(void *inputBuffer, void *outputBuffer,
#endif
- return(aResult);
+ return(aResult);
//#endif
} // end of WaveCallBack
@@ -335,7 +489,7 @@ static int wave_open_sound()
out_channels = 1;
#if USE_PORTAUDIO == 18
- // err = Pa_OpenDefaultStream(&pa_stream,0,1,paInt16,SAMPLE_RATE,FRAMES_PER_BUFFER,N_WAV_BUF,pa_callback,(void *)userdata);
+ // err = Pa_OpenDefaultStream(&pa_stream,0,1,paInt16,wave_samplerate,FRAMES_PER_BUFFER,N_WAV_BUF,pa_callback,(void *)userdata);
PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID();
@@ -351,12 +505,12 @@ static int wave_open_sound()
paInt16,
NULL,
/* general parameters */
- SAMPLE_RATE, FRAMES_PER_BUFFER, 0,
+ wave_samplerate, FRAMES_PER_BUFFER, 0,
//paClipOff | paDitherOff,
paNoFlag,
pa_callback, (void *)userdata);
-
- SHOW("wave_open_sound > Pa_OpenDefaultStream(1): err=%d (%s)\n",err, Pa_GetErrorText(err));
+
+ SHOW("wave_open_sound > Pa_OpenDefaultStream(1): err=%d (%s)\n",err, Pa_GetErrorText(err));
if(err == paInvalidChannelCount)
{
@@ -376,12 +530,12 @@ static int wave_open_sound()
paInt16,
NULL,
/* general parameters */
- SAMPLE_RATE, FRAMES_PER_BUFFER, 0,
+ wave_samplerate, FRAMES_PER_BUFFER, 0,
//paClipOff | paDitherOff,
paNoFlag,
pa_callback, (void *)userdata);
// err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,
-// SAMPLE_RATE,
+// wave_samplerate,
// FRAMES_PER_BUFFER,
// N_WAV_BUF,pa_callback,(void *)userdata);
SHOW("wave_open_sound > Pa_OpenDefaultStream(2): err=%d (%s)\n",err, Pa_GetErrorText(err));
@@ -395,13 +549,13 @@ static int wave_open_sound()
&pa_stream,
NULL, /* no input */
&myOutputParameters,
- SAMPLE_RATE,
+ wave_samplerate,
framesPerBuffer,
paNoFlag,
// paClipOff | paDitherOff,
pa_callback,
(void *)userdata);
- if ((err!=paNoError)
+ if ((err!=paNoError)
&& (err!=paInvalidChannelCount)) //err==paUnanticipatedHostError
{
fprintf(stderr, "wave_open_sound > Pa_OpenStream : err=%d (%s)\n",err,Pa_GetErrorText(err));
@@ -410,7 +564,7 @@ static int wave_open_sound()
&pa_stream,
NULL, /* no input */
&myOutputParameters,
- SAMPLE_RATE,
+ wave_samplerate,
framesPerBuffer,
paNoFlag,
// paClipOff | paDitherOff,
@@ -427,14 +581,14 @@ static int wave_open_sound()
&pa_stream,
NULL, /* no input */
&myOutputParameters,
- SAMPLE_RATE,
+ wave_samplerate,
framesPerBuffer,
paNoFlag,
// paClipOff | paDitherOff,
pa_callback,
(void *)userdata);
- // err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,(double)SAMPLE_RATE,FRAMES_PER_BUFFER,pa_callback,(void *)userdata);
+ // err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,(double)wave_samplerate,FRAMES_PER_BUFFER,pa_callback,(void *)userdata);
}
mInCallbackFinishedState = false;
#endif
@@ -457,13 +611,13 @@ static void update_output_parameters(int selectedDevice, const PaDeviceInfo *dev
myOutputParameters.channelCount = 1;
myOutputParameters.sampleFormat = paInt16;
- // Latency greater than 100ms for avoiding glitches
+ // Latency greater than 100ms for avoiding glitches
// (e.g. when moving a window in a graphical desktop)
// deviceInfo = Pa_GetDeviceInfo(selectedDevice);
if (deviceInfo)
{
double aLatency = deviceInfo->defaultLowOutputLatency;
- double aCoeff = round(0.100 / aLatency);
+// double aCoeff = round(0.100 / aLatency);
// myOutputParameters.suggestedLatency = aCoeff * aLatency; // to avoid glitches ?
myOutputParameters.suggestedLatency = aLatency; // for faster response ?
SHOW("Device=%d, myOutputParameters.suggestedLatency=%f, aCoeff=%f\n",
@@ -511,40 +665,40 @@ static void select_device(const char* the_api)
for( i=0; i<numDevices; i++ )
{
deviceInfo = Pa_GetDeviceInfo( i );
-
+
if (deviceInfo == NULL)
{
break;
}
const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
-
+
if (hostInfo && hostInfo->type == paALSA)
- {
+ {
// Check (once) the default output device
if (defaultAlsaIndex == numDevices)
{
defaultAlsaIndex = hostInfo->defaultOutputDevice;
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( defaultAlsaIndex );
update_output_parameters(defaultAlsaIndex, deviceInfo);
- if (Pa_IsFormatSupported(NULL, &myOutputParameters, SAMPLE_RATE) == 0)
+ if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0)
{
- SHOW( "select_device > ALSA (default), name=%s (#%d)\n", deviceInfo->name, defaultAlsaIndex);
+ SHOW( "select_device > ALSA (default), name=%s (#%d)\n", deviceInfo->name, defaultAlsaIndex);
selectedIndex = defaultAlsaIndex;
selectedDeviceInfo = deviceInfo;
break;
}
}
-
- // if the default output device does not match,
- // look for the device with the highest number of output channels
+
+ // if the default output device does not match,
+ // look for the device with the highest number of output channels
SHOW( "select_device > ALSA, i=%d (numDevices=%d)\n", i, numDevices);
-
+
update_output_parameters(i, deviceInfo);
-
- if (Pa_IsFormatSupported(NULL, &myOutputParameters, SAMPLE_RATE) == 0)
+
+ if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0)
{
SHOW( "select_device > ALSA, name=%s (#%d)\n", deviceInfo->name, i);
-
+
if (!selectedDeviceInfo
|| (selectedDeviceInfo->maxOutputChannels < deviceInfo->maxOutputChannels))
{
@@ -560,14 +714,14 @@ static void select_device(const char* the_api)
{
update_output_parameters(selectedIndex, selectedDeviceInfo);
}
- else
+ else
{
i = Pa_GetDefaultOutputDevice();
deviceInfo = Pa_GetDeviceInfo( i );
update_output_parameters(i, deviceInfo);
}
-#endif
+#endif
}
//>
@@ -613,12 +767,13 @@ void wave_set_callback_is_output_enabled(t_wave_callback* cb)
//<wave_init
// TBD: the arg could be "alsa", "oss",...
-void wave_init()
+int wave_init(int srate)
{
ENTER("wave_init");
PaError err;
pa_stream = NULL;
+ wave_samplerate = srate;
mInCallbackFinishedState = false;
init_buffer();
@@ -629,6 +784,7 @@ void wave_init()
{
SHOW_TIME("wave_init > Failed to initialise the PortAudio sound");
}
+ return err == paNoError;
}
//>
@@ -653,18 +809,18 @@ void* wave_open(const char* the_api)
//<copyBuffer
-static size_t copyBuffer(char* dest, char* src, const size_t theSizeInBytes)
-{
+static size_t copyBuffer(char* dest, char* src, const size_t theSizeInBytes)
+{
size_t bytes_written = 0;
unsigned int i = 0;
uint16_t* a_dest = NULL;
uint16_t* a_src = NULL;
-
+
if ((src != NULL) && dest != NULL)
- {
+ {
// copy for one channel (mono)?
if(out_channels==1)
- {
+ {
SHOW("copyBuffer > 1 channel > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes);
memcpy(dest, src, theSizeInBytes);
bytes_written = theSizeInBytes;
@@ -675,7 +831,7 @@ static size_t copyBuffer(char* dest, char* src, const size_t theSizeInBytes)
i = 0;
a_dest = (uint16_t* )dest;
a_src = (uint16_t* )src;
-
+
for(i=0; i<theSizeInBytes/2; i++)
{
a_dest[2*i] = a_src[i];
@@ -684,8 +840,8 @@ static size_t copyBuffer(char* dest, char* src, const size_t theSizeInBytes)
bytes_written = 2*theSizeInBytes;
} // end if(out_channels==1)
} // end if ((src != NULL) && dest != NULL)
-
- return bytes_written;
+
+ return bytes_written;
}
//>
@@ -698,7 +854,7 @@ size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSiz
// space in ringbuffer for the sample needed: 1x mono channel but 2x for 1 stereo channel
size_t bytes_to_write = (out_channels==1) ? theSize : theSize*2;
my_stream_could_start = 0;
-
+
if(pa_stream == NULL)
{
SHOW_TIME("wave_write > wave_open_sound\n");
@@ -714,16 +870,16 @@ size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSiz
my_stream_could_start = 1;
}
assert(BUFFER_LENGTH >= bytes_to_write);
-
+
if (myWrite >= myBuffer + BUFFER_LENGTH)
{
myWrite = myBuffer;
} // end if (myWrite >= myBuffer + BUFFER_LENGTH)
-
+
size_t aTotalFreeMem=0;
char* aRead = myRead;
SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite);
-
+
while (1)
{
if (my_callback_is_output_enabled && (0==my_callback_is_output_enabled()))
@@ -731,9 +887,9 @@ size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSiz
SHOW_TIME("wave_write > my_callback_is_output_enabled: no!");
return 0;
}
-
+
aRead = myRead;
-
+
// write pointer is before read pointer?
if (myWrite >= aRead)
{
@@ -743,27 +899,27 @@ size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSiz
{
aTotalFreeMem = aRead - myWrite;
} // end if (myWrite >= aRead)
-
+
if (aTotalFreeMem>1)
{
// -1 because myWrite must be different of aRead
// otherwise buffer would be considered as empty
aTotalFreeMem -= 1;
} // end if (aTotalFreeMem>1)
-
+
if (aTotalFreeMem >= bytes_to_write)
{
break;
} // end if (aTotalFreeMem >= bytes_to_write)
-
+
//SHOW_TIME("wave_write > wait");
SHOW("wave_write > wait: aTotalFreeMem=%d\n", aTotalFreeMem);
SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite);
usleep(10000);
} // end while (1)
-
+
aRead = myRead;
-
+
// write pointer is ahead the read pointer?
if (myWrite >= aRead)
{
@@ -800,17 +956,17 @@ size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSiz
SHOW_TIME("wave_write > myWrite <= aRead");
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize);
} // end if (myWrite >= aRead)
-
+
bytes_written = bytes_to_write;
myWritePosition += theSize/sizeof(uint16_t); // add number of samples
-
+
if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER))
{
start_stream();
} // end if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER))
-
+
SHOW_TIME("wave_write > LEAVE");
-
+
return bytes_written;
}
@@ -829,7 +985,7 @@ int wave_close(void* theHandler)
SHOW_TIME("wave_close > LEAVE (NULL stream)");
return 0;
}
-
+
if( Pa_IsStreamStopped( pa_stream ) )
{
SHOW_TIME("wave_close > LEAVE (stopped)");
@@ -841,7 +997,7 @@ int wave_close(void* theHandler)
SHOW_TIME("wave_close > LEAVE (NULL stream)");
return 0;
}
-
+
if( Pa_StreamActive( pa_stream ) == false && mInCallbackFinishedState == false )
{
SHOW_TIME("wave_close > LEAVE (not active)");
@@ -857,20 +1013,20 @@ int wave_close(void* theHandler)
SHOW_TIME("wave_close > LEAVE (stopStreamCount)");
return 0;
}
-
- // Comment from Audacity-1.2.4b adapted to the eSpeak context.
+
+ // Comment from Audacity-1.2.4b adapted to the eSpeak context.
//
// We got here in one of two ways:
//
- // 1. The calling program calls the espeak_Cancel function and we
- // therefore want to stop as quickly as possible.
+ // 1. The calling program calls the espeak_Cancel function and we
+ // therefore want to stop as quickly as possible.
// So we use AbortStream(). If this is
// the case the portaudio stream is still in the Running state
// (see PortAudio state machine docs).
//
// 2. The callback told PortAudio to stop the stream since it had
- // reached the end of the selection.
- // The event polling thread discovered this by noticing that
+ // reached the end of the selection.
+ // The event polling thread discovered this by noticing that
// wave_is_busy() returned false.
// wave_is_busy() (which calls Pa_GetStreamActive()) will not return
// false until all buffers have finished playing, so we can call
@@ -885,7 +1041,7 @@ int wave_close(void* theHandler)
// call StopStream if the callback brought us here, and AbortStream
// if the user brought us here.
//
-
+
#if (USE_PORTAUDIO == 19)
if (pa_stream)
{
@@ -969,14 +1125,14 @@ int wave_is_busy(void* theHandler)
if (pa_stream)
{
#if USE_PORTAUDIO == 18
- active = Pa_StreamActive(pa_stream)
+ active = Pa_StreamActive(pa_stream)
&& (mInCallbackFinishedState == false);
#else
active = Pa_IsStreamActive(pa_stream)
&& (mInCallbackFinishedState == false);
#endif
}
-
+
SHOW("wave_is_busy: %d\n",active);
@@ -1015,7 +1171,7 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
if (!time || !pa_stream)
{
- SHOW("event get_remaining_time> %s\n","audio device not available");
+ SHOW("event get_remaining_time> %s\n","audio device not available");
return -1;
}
@@ -1023,7 +1179,7 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
{
// TBD: take in account time suplied by portaudio V18 API
a_time = sample - myReadPosition;
- a_time = 0.5 + (a_time * 1000.0) / SAMPLE_RATE;
+ a_time = 0.5 + (a_time * 1000.0) / wave_samplerate;
}
else
{
@@ -1050,7 +1206,7 @@ void *wave_test_get_write_buffer()
// notdef USE_PORTAUDIO
-void wave_init() {}
+int wave_init(int srate) {return 1;}
void* wave_open(const char* the_api) {return (void *)1;}
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {return theSize;}
int wave_close(void* theHandler) {return 0;}
@@ -1101,7 +1257,7 @@ void add_time_in_ms(struct timespec *ts, int time_in_ms)
{
SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns);
ts->tv_sec += 1;
- t_ns -= ONE_BILLION;
+ t_ns -= ONE_BILLION;
}
ts->tv_nsec = (long int)t_ns;
}
diff --git a/navit/support/espeak/wave.h b/navit/support/espeak/wave.h
index de8bf1ef8..d1b04a3ab 100644
--- a/navit/support/espeak/wave.h
+++ b/navit/support/espeak/wave.h
@@ -1,22 +1,13 @@
#ifndef WAVE_H
#define WAVE_H
-#ifdef _MSC_VER
-
-typedef __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int32 uint64_t;
-
-#else
#ifndef PLATFORM_DOS
#include "stdint.h"
#endif
-#endif
extern int option_device_number;
-extern void wave_init();
+extern int wave_init(int samplerate);
// TBD: the arg could be "alsa", "oss",...
extern void* wave_open(const char* the_api);
@@ -24,7 +15,7 @@ extern size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t
extern int wave_close(void* theHandler);
extern void wave_flush(void* theHandler);
extern int wave_is_busy(void* theHandler);
-extern void wave_terminate();
+extern void wave_terminate(void);
extern uint32_t wave_get_read_position(void* theHandler);
extern uint32_t wave_get_write_position(void* theHandler);
@@ -47,6 +38,6 @@ extern void clock_gettime2(struct timespec *ts);
extern void add_time_in_ms(struct timespec *ts, int time_in_ms);
// for tests
-extern void *wave_test_get_write_buffer();
+extern void *wave_test_get_write_buffer(void);
#endif
diff --git a/navit/support/espeak/wave_pulse.c b/navit/support/espeak/wave_pulse.c
index d5fbc8c1e..1bcb593a7 100755..100644
--- a/navit/support/espeak/wave_pulse.c
+++ b/navit/support/espeak/wave_pulse.c
@@ -75,21 +75,52 @@ static t_wave_callback* my_callback_is_output_enabled=NULL;
#define MINREQ 880
#define FRAGSIZE 0
+#ifdef USE_PORTAUDIO
+// rename functions to be wrapped
+#define wave_init wave_pulse_init
+#define wave_open wave_pulse_open
+#define wave_write wave_pulse_write
+#define wave_close wave_pulse_close
+#define wave_is_busy wave_pulse_is_busy
+#define wave_terminate wave_pulse_terminate
+#define wave_get_read_position wave_pulse_get_read_position
+#define wave_get_write_position wave_pulse_get_write_position
+#define wave_flush wave_pulse_flush
+#define wave_set_callback_is_output_enabled wave_pulse_set_callback_is_output_enabled
+#define wave_test_get_write_buffer wave_pulse_test_get_write_buffer
+#define wave_get_remaining_time wave_pulse_get_remaining_time
+
+// check whether we can connect to PulseAudio
+#include <pulse/simple.h>
+int is_pulse_running()
+{
+ pa_sample_spec ss;
+ ss.format = ESPEAK_FORMAT;
+ ss.rate = SAMPLE_RATE;
+ ss.channels = ESPEAK_CHANNEL;
+
+ pa_simple *s = pa_simple_new(NULL, "eSpeak", PA_STREAM_PLAYBACK, NULL, "is_pulse_running", &ss, NULL, NULL, NULL);
+ if (s) {
+ pa_simple_free(s);
+ return 1;
+ } else
+ return 0;
+}
+#endif // USE_PORTAUDIO
+
static pthread_mutex_t pulse_mutex;
static pa_context *context = NULL;
static pa_stream *stream = NULL;
static pa_threaded_mainloop *mainloop = NULL;
-static pa_cvolume volume;
-static int volume_valid = 0;
-
static int do_trigger = 0;
static uint64_t written = 0;
static int time_offset_msec = 0;
static int just_flushed = 0;
static int connected = 0;
+static int wave_samplerate;
#define CHECK_DEAD_GOTO(label, warn) do { \
if (!mainloop || \
@@ -131,20 +162,7 @@ do { \
// SHOW("ti> read_index=0x%lx\n",the_time->read_index);
// }
-
-static void info_cb(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
- ENTER(__FUNCTION__);
- assert(c);
-
- if (!i)
- return;
-
- volume = i->volume;
- volume_valid = 1;
-}
-
static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) {
- pa_operation *o;
ENTER(__FUNCTION__);
assert(c);
@@ -154,13 +172,6 @@ static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t
(t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW)))
return;
-
- if (!(o = pa_context_get_sink_input_info(c, index, info_cb, NULL))) {
- SHOW("pa_context_get_sink_input_info() failed: %s\n", pa_strerror(pa_context_errno(c)));
- return;
- }
-
- pa_operation_unref(o);
}
static void context_state_cb(pa_context *c, void *userdata) {
@@ -367,7 +378,7 @@ static void pulse_write(void* ptr, int length) {
SHOW("pulse_write > length=%d\n", length);
- CHECK_CONNECTED();
+ CHECK_CONNECTED_NO_RETVAL();
pa_threaded_mainloop_lock(mainloop);
CHECK_DEAD_GOTO(fail, 1);
@@ -475,18 +486,12 @@ static int pulse_open()
pthread_mutex_init( &pulse_mutex, (const pthread_mutexattr_t *)NULL);
ss.format = ESPEAK_FORMAT;
- ss.rate = SAMPLE_RATE;
+ ss.rate = wave_samplerate;
ss.channels = ESPEAK_CHANNEL;
if (!pa_sample_spec_valid(&ss))
return false;
-/* if (!volume_valid) { */
- pa_cvolume_reset(&volume, ss.channels);
- volume_valid = 1;
-/* } else if (volume.channels != ss.channels) */
-/* pa_cvolume_set(&volume, ss.channels, pa_cvolume_avg(&volume)); */
-
SHOW_TIME("pa_threaded_mainloop_new (call)");
if (!(mainloop = pa_threaded_mainloop_new())) {
SHOW("Failed to allocate main loop\n","");
@@ -539,8 +544,6 @@ static int pulse_open()
pa_stream_set_write_callback(stream, stream_request_cb, NULL);
pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL);
-
-
pa_buffer_attr a_attr;
a_attr.maxlength = MAXLENGTH;
@@ -550,7 +553,7 @@ static int pulse_open()
a_attr.fragsize = 0;
SHOW_TIME("pa_connect_playback");
- if (pa_stream_connect_playback(stream, NULL, &a_attr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE), &volume, NULL) < 0) {
+ if (pa_stream_connect_playback(stream, NULL, &a_attr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL) < 0) {
SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
goto unlock_and_fail;
}
@@ -578,44 +581,24 @@ static int pulse_open()
pa_threaded_mainloop_wait(mainloop);
}
- if (!success) {
- SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
- goto unlock_and_fail;
- }
-
pa_operation_unref(o);
- /* Now request the initial stream info */
- if (!(o = pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_cb, NULL))) {
- SHOW("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context)));
+ if (!success) {
+ SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
goto unlock_and_fail;
}
-
- SHOW_TIME("pa_threaded_mainloop_wait 2");
- while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
- CHECK_DEAD_GOTO(fail, 1);
- pa_threaded_mainloop_wait(mainloop);
- }
-
-/* if (!volume_valid) { */
-/* SHOW("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context))); */
-/* goto unlock_and_fail; */
-/* } */
do_trigger = 0;
written = 0;
time_offset_msec = 0;
just_flushed = 0;
connected = 1;
- // volume_time_event = NULL;
pa_threaded_mainloop_unlock(mainloop);
SHOW_TIME("pulse_open (ret true)");
- // return true;
return PULSE_OK;
-
unlock_and_fail:
if (o)
@@ -677,13 +660,14 @@ void wave_set_callback_is_output_enabled(t_wave_callback* cb)
//>
//<wave_init
-void wave_init()
+int wave_init(int srate)
{
ENTER("wave_init");
stream = NULL;
+ wave_samplerate = srate;
- pulse_open();
+ return pulse_open() == PULSE_OK;
}
//>
@@ -761,19 +745,32 @@ size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSiz
int wave_close(void* theHandler)
{
SHOW_TIME("wave_close > ENTER");
+ static int aStopStreamCount = 0;
- int a_status = pthread_mutex_lock(&pulse_mutex);
- if (a_status) {
- SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__);
- return PULSE_ERROR;
- }
-
- drain();
+ // Avoid race condition by making sure this function only
+ // gets called once at a time
+ aStopStreamCount++;
+ if (aStopStreamCount != 1)
+ {
+ SHOW_TIME("wave_close > LEAVE (stopStreamCount)");
+ return 0;
+ }
- pthread_mutex_unlock(&pulse_mutex);
- SHOW_TIME("wave_close (ret)");
+ int a_status = pthread_mutex_lock(&pulse_mutex);
+ if (a_status)
+ {
+ SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__);
+ aStopStreamCount = 0; // last action
+ return PULSE_ERROR;
+ }
+
+ drain();
+
+ pthread_mutex_unlock(&pulse_mutex);
+ SHOW_TIME("wave_close (ret)");
- return PULSE_OK;
+ aStopStreamCount = 0; // last action
+ return PULSE_OK;
}
//>
@@ -846,7 +843,7 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
{
// TBD: take in account time suplied by portaudio V18 API
a_time = sample - a_timing_info.read_index;
- a_time = 0.5 + (a_time * 1000.0) / SAMPLE_RATE;
+ a_time = 0.5 + (a_time * 1000.0) / wave_samplerate;
}
else
{
@@ -873,7 +870,7 @@ void *wave_test_get_write_buffer()
// notdef USE_PULSEAUDIO
-void wave_init() {}
+int wave_init(return 1;) {}
void* wave_open(const char* the_api) {return (void *)1;}
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {return theSize;}
int wave_close(void* theHandler) {return 0;}
@@ -893,8 +890,9 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
return 0;
}
-#endif // of USE_PORTAUDIO
+#endif // of USE_PULSEAUDIO
+#ifndef USE_PORTAUDIO
//>
//<clock_gettime2, add_time_in_ms
@@ -928,6 +926,7 @@ void add_time_in_ms(struct timespec *ts, int time_in_ms)
}
ts->tv_nsec = (long int)t_ns;
}
+#endif // ifndef USE_PORTAUDIO
#endif // USE_ASYNC
diff --git a/navit/support/espeak/wave_sada.c b/navit/support/espeak/wave_sada.c
index c69a4dc99..23d53c778 100755..100644
--- a/navit/support/espeak/wave_sada.c
+++ b/navit/support/espeak/wave_sada.c
@@ -63,6 +63,7 @@ static uint32_t total_samples_skipped;
//
static uint32_t last_play_position=0;
+static uint32_t wave_samplerate;
//>
// wave_init
//
@@ -77,12 +78,14 @@ static uint32_t last_play_position=0;
//
//<wave_init
-void wave_init() {
+int wave_init(int srate) {
ENTER("wave_init");
audio_info_t ainfo;
char *audio_device = NULL;
+ wave_samplerate = srate;
+
audio_device = getenv("AUDIODEV");
if (audio_device != NULL) {
if ((sun_audio_fd = open(audio_device, O_WRONLY)) < 0) {
@@ -101,21 +104,22 @@ void wave_init() {
SHOW("wave_init() sun_audio_fd: %d\n", sun_audio_fd);
if (sun_audio_fd < 0) {
- return;
+ return(0);
}
ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo);
SHOW("wave_init() play buffer size: %d\n", ainfo.play.buffer_size);
ainfo.play.encoding = AUDIO_ENCODING_LINEAR;
ainfo.play.channels = 1;
- ainfo.play.sample_rate = SAMPLE_RATE;
+ ainfo.play.sample_rate = wave_samplerate;
ainfo.play.precision = SAMPLE_SIZE;
if (ioctl(sun_audio_fd, AUDIO_SETINFO, &ainfo) == -1) {
SHOW("wave_init() failed to set audio params: %s\n", strerror(errno));
close(sun_audio_fd);
- return;
+ return(0);
}
+ return(1);
}
//>
@@ -306,7 +310,11 @@ int wave_close(void* theHandler)
int wave_is_busy(void* theHandler)
{
uint32_t time;
- wave_get_remaining_time(total_samples_sent - 1, &time);
+ if (total_samples_sent >= 1) {
+ wave_get_remaining_time(total_samples_sent - 1, &time);
+ } else {
+ time = 0;
+ }
return time != 0;
}
@@ -516,7 +524,7 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
(actual_index <= ainfo.play.samples)) {
*time = 0;
} else {
- a_time = ((actual_index - ainfo.play.samples) * 1000) / SAMPLE_RATE;
+ a_time = ((actual_index - ainfo.play.samples) * 1000) / wave_samplerate;
*time = (uint32_t) a_time;
}
SHOW("wave_get_remaining_time for %d: %d\n", sample, *time);
@@ -527,7 +535,7 @@ int wave_get_remaining_time(uint32_t sample, uint32_t* time)
#else
// notdef USE_SADA
-void wave_init() {}
+init wave_init() {return 1;}
void* wave_open(const char* the_api) {return (void *)1;}
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {return theSize;}
int wave_close(void* theHandler) {return 0;}
diff --git a/navit/support/espeak/wavegen.c b/navit/support/espeak/wavegen.c
index a5467ca1e..057178b70 100755..100644
--- a/navit/support/espeak/wavegen.c
+++ b/navit/support/espeak/wavegen.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
+ * Copyright (C) 2005 to 2013 by Jonathan Duddington *
* email: jonsd@users.sourceforge.net *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -28,13 +28,17 @@
#include <math.h>
+
#include "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "voice.h"
-//#undef INCLUDE_KLATT
+#include "wavegen.h"
+#ifdef INCLUDE_SONIC
+#include "sonic.h"
+#endif
#ifdef USE_PORTAUDIO
#include "portaudio.h"
@@ -78,12 +82,11 @@ static wavegen_peaks_t peaks[N_PEAKS];
static int peak_harmonic[N_PEAKS];
static int peak_height[N_PEAKS];
-#define N_ECHO_BUF 5500 // max of 250mS at 22050 Hz
-static int echo_head;
-static int echo_tail;
+int echo_head;
+int echo_tail;
+int echo_amp = 0;
+short echo_buf[N_ECHO_BUF];
static int echo_length = 0; // period (in sample\) to ensure completion of echo at the end of speech, set in WavegenSetEcho()
-static int echo_amp = 0;
-static short echo_buf[N_ECHO_BUF];
static int voicing;
static RESONATOR rbreath[N_PEAKS];
@@ -129,55 +132,31 @@ unsigned char *out_end;
int outbuf_size = 0;
// the queue of operations passed to wavegen from sythesize
-long wcmdq[N_WCMDQ][4];
+long64 wcmdq[N_WCMDQ][4];
int wcmdq_head=0;
int wcmdq_tail=0;
// pitch,speed,
-int embedded_default[N_EMBEDDED_VALUES] = {0,50,170,100,50, 0,0, 0,170,0,0,0,0,0};
-static int embedded_max[N_EMBEDDED_VALUES] = {0,0x7fff,600,300,99,99,99, 0,600,0,0,0,0,4};
+int embedded_default[N_EMBEDDED_VALUES] = {0, 50,175,100,50, 0, 0, 0,175,0,0,0,0,0,0};
+static int embedded_max[N_EMBEDDED_VALUES] = {0,0x7fff,750,300,99,99,99, 0,750,0,0,0,0,4,0};
#define N_CALLBACK_IX N_WAV_BUF-2 // adjust this delay to match display with the currently spoken word
int current_source_index=0;
extern FILE *f_wave;
-
+#ifdef USE_PORTAUDIO
#if (USE_PORTAUDIO == 18)
static PortAudioStream *pa_stream=NULL;
#endif
#if (USE_PORTAUDIO == 19)
static PaStream *pa_stream=NULL;
#endif
+#endif
-/* default pitch envelope, a steady fall */
-#define ENV_LEN 128
-
-#define int(x) (int)(x)
-/*
-unsigned char Pitch_env0[ENV_LEN] = {
- 255,253,251,249,247,245,243,241,239,237,235,233,231,229,227,225,
- 223,221,219,217,215,213,211,209,207,205,203,201,199,197,195,193,
- 191,189,187,185,183,181,179,177,175,173,171,169,167,165,163,161,
- 159,157,155,153,151,149,147,145,143,141,139,137,135,133,131,129,
- 127,125,123,121,119,117,115,113,111,109,107,105,103,101, 99, 97,
- 95, 93, 91, 89, 87, 85, 83, 81, 79, 77, 75, 73, 71, 69, 67, 65,
- 63, 61, 59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 39, 37, 35, 33,
- 31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1
-};
-*/
-
-/*
-unsigned char Pitch_long[ENV_LEN] = {
- 254,249,250,251,252,253,254,254, 255,255,255,255,254,254,253,252,
- 251,250,249,247,244,242,238,234, 230,225,221,217,213,209,206,203,
- 199,195,191,187,183,179,175,172, 168,165,162,159,156,153,150,148,
- 145,143,140,138,136,134,132,130, 128,126,123,120,117,114,111,107,
- 104,100,96,91, 86,82,77,73, 70,66,63,60, 58,55,53,51,
- 49,47,46,45, 43,42,40,38, 36,34,31,28, 26,24,22,20,
- 18,16,14,12, 11,10,9,8, 8,8,8,8, 9,8,8,8,
- 8,8,7,7, 6,6,6,5, 4,4,3,3, 2,1,1,0
-};
-*/
+#ifdef INCLUDE_SONIC
+static sonicStream sonicSpeedupStream = NULL;
+double sonicSpeed = 1.0;
+#endif
// 1st index=roughness
// 2nd index=modulation_type
@@ -267,7 +246,7 @@ static unsigned char wavemult[N_WAVEMULT] = {
105, 98, 90, 83, 76, 69, 62, 55, 49, 43, 37, 32, 27, 22, 18, 14,
11, 8, 5, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
+
// set from y = pow(2,x) * 128, x=-1 to 1
unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1] = {
@@ -285,12 +264,13 @@ unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1] = {
217,220,223,226,229,232,236,239,
242,246,249,252, 254,255 };
-int WavegenFill(int fill_zeros);
-
#ifdef LOG_FRAMES
-static void LogMarker(int type, int value)
-{//=======================================
+static void LogMarker(int type, int value, int value2)
+{//===================================================
+ char buf[20];
+ int *p;
+
if(option_log_frames == 0)
return;
@@ -300,7 +280,13 @@ static void LogMarker(int type, int value)
if(f_log)
{
if(type == espeakEVENT_PHONEME)
- fprintf(f_log,"Phoneme [%s]\n",WordToString(value));
+ {
+ p = (int *)buf;
+ p[0] = value;
+ p[1] = value2;
+ buf[8] = 0;
+ fprintf(f_log,"Phoneme [%s]\n", buf);
+ }
else
fprintf(f_log,"\n");
fclose(f_log);
@@ -310,17 +296,28 @@ static void LogMarker(int type, int value)
}
#endif
-void WcmdqStop()
+void WcmdqStop(void)
{//=============
wcmdq_head = 0;
wcmdq_tail = 0;
+
+#ifdef INCLUDE_SONIC
+ if(sonicSpeedupStream != NULL)
+ {
+ sonicDestroyStream(sonicSpeedupStream);
+ sonicSpeedupStream = NULL;
+ }
+#endif
+
#ifdef USE_PORTAUDIO
Pa_AbortStream(pa_stream);
#endif
+ if(mbrola_name[0] != 0)
+ MbrolaReset();
}
-int WcmdqFree()
+int WcmdqFree(void)
{//============
int i;
i = wcmdq_head - wcmdq_tail;
@@ -328,19 +325,19 @@ int WcmdqFree()
return(i);
}
-int WcmdqUsed()
+int WcmdqUsed(void)
{//============
return(N_WCMDQ - WcmdqFree());
}
-void WcmdqInc()
+void WcmdqInc(void)
{//============
wcmdq_tail++;
if(wcmdq_tail >= N_WCMDQ) wcmdq_tail=0;
}
-static void WcmdqIncHead()
+static void WcmdqIncHead(void)
{//=======================
wcmdq_head++;
if(wcmdq_head >= N_WCMDQ) wcmdq_head=0;
@@ -350,12 +347,14 @@ static void WcmdqIncHead()
// data points from which to make the presets for pk_shape1 and pk_shape2
#define PEAKSHAPEW 256
+#ifdef deleted
static const float pk_shape_x[2][8] = {
{0,-0.6f, 0.0f, 0.6f, 1.4f, 2.5f, 4.5f, 5.5f},
{0,-0.6f, 0.0f, 0.6f, 1.4f, 2.0f, 4.5f, 5.5f }};
static const float pk_shape_y[2][8] = {
{0, 67, 81, 67, 31, 14, 0, -6} ,
{0, 77, 81, 77, 31, 7, 0, -6 }};
+#endif
unsigned char pk_shape1[PEAKSHAPEW+1] = {
255,254,254,254,254,254,253,253,252,251,251,250,249,248,247,246,
@@ -440,6 +439,10 @@ static int userdata[4];
static PaError pa_init_err=0;
static int out_channels=1;
+unsigned char *outbuffer = NULL;
+int outbuffer_size = 0;
+
+
#if USE_PORTAUDIO == 18
static int WaveCallback(void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
@@ -452,9 +455,30 @@ static int WaveCallback(const void *inputBuffer, void *outputBuffer,
int ix;
int result;
unsigned char *p;
+ unsigned char *out_buf;
+ unsigned char *out_end2;
+ int pa_size;
- out_ptr = out_start = (unsigned char *)outputBuffer;
- out_end = out_ptr + framesPerBuffer*2;
+ pa_size = framesPerBuffer*2;
+
+ // make a buffer 3x size of the portaudio output
+ ix = pa_size*3;
+ if(ix > outbuffer_size)
+ {
+ outbuffer = (unsigned char *)realloc(outbuffer, ix);
+ if(outbuffer == NULL)
+ {
+ fprintf(stderr, "espeak: out of memory\n");
+ }
+ outbuffer_size = ix;
+ out_ptr = NULL;
+ }
+ if(out_ptr == NULL)
+ {
+ out_ptr = out_start = outbuffer;
+ out_end = out_start + outbuffer_size;
+ }
+ out_end2 = &outbuffer[pa_size]; // top of data needed for the portaudio buffer
#ifdef LIBRARY
event_list_ix = 0;
@@ -462,6 +486,24 @@ static int WaveCallback(const void *inputBuffer, void *outputBuffer,
result = WavegenFill(1);
+ // copy from the outbut buffer into the portaudio buffer
+ if(result && (out_ptr > out_end2))
+ {
+ result = 0; // don't end yet, there is more data in the buffer than can fit in portaudio
+ }
+
+ while(out_ptr < out_end2)
+ *out_ptr++ = 0; // fill with zeros up to the size of the portaudio buffer
+
+ memcpy(outputBuffer, outbuffer, pa_size);
+
+ // move the remaining contents of the start of the output buffer
+ for(p = out_end2; p < out_end; p++)
+ {
+ p[-pa_size] = p[0];
+ }
+ out_ptr -= pa_size;
+
#ifdef LIBRARY
count_samples += framesPerBuffer;
if(synth_callback)
@@ -482,14 +524,15 @@ static int WaveCallback(const void *inputBuffer, void *outputBuffer,
{
// swap the order of bytes in each sound sample in the portaudio buffer
int c;
- out_ptr = (unsigned char *)outputBuffer;
- out_end = out_ptr + framesPerBuffer*2;
- while(out_ptr < out_end)
+ unsigned char *buf_end;
+ out_buf = (unsigned char *)outputBuffer;
+ buf_end = out_buf + framesPerBuffer*2;
+ while(out_buf < buf_end)
{
- c = out_ptr[0];
- out_ptr[0] = out_ptr[1];
- out_ptr[1] = c;
- out_ptr += 2;
+ c = out_buf[0];
+ out_buf[0] = out_buf[1];
+ out_buf[1] = c;
+ out_buf += 2;
}
}
#endif
@@ -498,12 +541,12 @@ static int WaveCallback(const void *inputBuffer, void *outputBuffer,
{
// sound output can only do stereo, not mono. Duplicate each sound sample to
// produce 2 channels.
- out_ptr = (unsigned char *)outputBuffer;
+ out_buf = (unsigned char *)outputBuffer;
for(ix=framesPerBuffer-1; ix>=0; ix--)
{
- p = &out_ptr[ix*4];
- p[3] = p[1] = out_ptr[ix*2 + 1];
- p[2] = p[0] = out_ptr[ix*2];
+ p = &out_buf[ix*4];
+ p[3] = p[1] = out_buf[ix*2 + 1];
+ p[2] = p[0] = out_buf[ix*2];
}
}
@@ -553,7 +596,7 @@ static PaError Pa_OpenDefaultStream2( PaStream** stream,
hostApiOutputParameters.device = Pa_GetDefaultOutputDevice();
if( hostApiOutputParameters.device == paNoDevice )
- return paDeviceUnavailable;
+ return paDeviceUnavailable;
hostApiOutputParameters.channelCount = outputChannelCount;
hostApiOutputParameters.sampleFormat = sampleFormat;
@@ -574,7 +617,7 @@ static PaError Pa_OpenDefaultStream2( PaStream** stream,
#endif
-int WavegenOpenSound()
+int WavegenOpenSound(void)
{//===================
PaError err, err2;
PaError active;
@@ -639,7 +682,7 @@ int WavegenOpenSound()
-int WavegenCloseSound()
+int WavegenCloseSound(void)
{//====================
PaError active;
@@ -669,7 +712,7 @@ int WavegenCloseSound()
}
-int WavegenInitSound()
+int WavegenInitSound(void)
{//===================
PaError err;
@@ -687,15 +730,15 @@ int WavegenInitSound()
return(0);
}
#else
-int WavegenOpenSound()
+int WavegenOpenSound(void)
{//===================
return(0);
}
-int WavegenCloseSound()
+int WavegenCloseSound(void)
{//====================
return(0);
}
-int WavegenInitSound()
+int WavegenInitSound(void)
{//===================
return(0);
}
@@ -720,7 +763,7 @@ void WavegenInit(int rate, int wavemult_fact)
max_hval = 0;
wdata.amplitude = 32;
- wdata.prev_was_synth = 0;
+ wdata.amplitude_fmt = 100;
for(ix=0; ix<N_EMBEDDED_VALUES; ix++)
embedded_value[ix] = embedded_default[ix];
@@ -754,6 +797,7 @@ void WavegenInit(int rate, int wavemult_fact)
#ifdef LOG_FRAMES
remove("log-espeakedit");
+remove("log-klatt");
#endif
} // end of WavegenInit
@@ -794,12 +838,6 @@ static void WavegenSetEcho(void)
amp = embedded_value[EMBED_H];
delay = 130;
}
- if(embedded_value[EMBED_T] > 0)
- {
- // announcing punctuation
- amp = embedded_value[EMBED_T] * 8;
- delay = 60;
- }
if(delay == 0)
amp = 0;
@@ -811,7 +849,7 @@ static void WavegenSetEcho(void)
if(amp > 20)
echo_length = echo_head * 2; // perhaps allow 2 echo periods if the echo is loud.
- // echo_amp units are 1/256ths of the amplitude of the original sound.
+ // echo_amp units are 1/256ths of the amplitude of the original sound.
echo_amp = amp;
// compensate (partially) for increase in amplitude due to echo
general_amplitude = GetAmplitude();
@@ -820,7 +858,7 @@ static void WavegenSetEcho(void)
-int PeaksToHarmspect(wavegen_peaks_t *peaks, int pitch, int *htab, int control)
+static int PeaksToHarmspect(wavegen_peaks_t *peaks, int pitch, int *htab, int control)
{//============================================================================
// Calculate the amplitude of each harmonics from the formants
// Only for formants 0 to 5
@@ -951,7 +989,7 @@ int h2;
-static void AdvanceParameters()
+static void AdvanceParameters(void)
{//============================
// Called every 64 samples to increment the formant freq, height, and widths
@@ -982,16 +1020,16 @@ static void AdvanceParameters()
for(ix=0; ix <= wvoice->n_harmonic_peaks; ix++)
{
peaks[ix].freq1 += peaks[ix].freq_inc;
- peaks[ix].freq = (int)(peaks[ix].freq1);
+ peaks[ix].freq = (int)peaks[ix].freq1;
peaks[ix].height1 += peaks[ix].height_inc;
- if((peaks[ix].height = (int)(peaks[ix].height1)) < 0)
+ if((peaks[ix].height = (int)peaks[ix].height1) < 0)
peaks[ix].height = 0;
peaks[ix].left1 += peaks[ix].left_inc;
- peaks[ix].left = (int)(peaks[ix].left1);
+ peaks[ix].left = (int)peaks[ix].left1;
if(ix < 3)
{
peaks[ix].right1 += peaks[ix].right_inc;
- peaks[ix].right = (int)(peaks[ix].right1);
+ peaks[ix].right = (int)peaks[ix].right1;
}
else
{
@@ -1004,10 +1042,10 @@ static void AdvanceParameters()
if(ix < 7)
{
peaks[ix].freq1 += peaks[ix].freq_inc;
- peaks[ix].freq = (int)(peaks[ix].freq1);
+ peaks[ix].freq = (int)peaks[ix].freq1;
}
peaks[ix].height1 += peaks[ix].height_inc;
- if((peaks[ix].height = (int)(peaks[ix].height1)) < 0)
+ if((peaks[ix].height = (int)peaks[ix].height1) < 0)
peaks[ix].height = 0;
}
@@ -1086,7 +1124,7 @@ void InitBreath(void)
-static void SetBreath()
+static void SetBreath(void)
{//====================
#ifndef PLATFORM_RISCOS
int pk;
@@ -1123,7 +1161,7 @@ static int ApplyBreath(void)
if((amp = wvoice->breath[ix]) != 0)
{
amp *= (peaks[ix].height >> 14);
- value += (int)(resonator(&rbreath[ix],noise) * amp);
+ value += (int)resonator(&rbreath[ix],noise) * amp;
}
}
#endif
@@ -1132,7 +1170,7 @@ static int ApplyBreath(void)
-int Wavegen()
+static int Wavegen(void)
{//==========
unsigned short waveph;
unsigned short theta;
@@ -1171,7 +1209,8 @@ int Wavegen()
maxh2 = PeaksToHarmspect(peaks, wdata.pitch<<4, hspect[0], 0);
// adjust amplitude to compensate for fewer harmonics at higher pitch
- amplitude2 = (wdata.amplitude * wdata.pitch)/(100 << 11);
+// amplitude2 = (wdata.amplitude * wdata.pitch)/(100 << 11);
+ amplitude2 = (wdata.amplitude * (wdata.pitch >> 8) * wdata.amplitude_fmt)/(10000 << 3);
// switch sign of harmonics above about 900Hz, to reduce max peak amplitude
h_switch_sign = 890 / (wdata.pitch >> 12);
@@ -1220,11 +1259,12 @@ int Wavegen()
for(pk=wvoice->n_harmonic_peaks+1; pk<N_PEAKS; pk++)
{
// find the nearest harmonic for HF peaks where we don't use shape
- peak_harmonic[pk] = peaks[pk].freq / (wdata.pitch*16);
+ peak_harmonic[pk] = ((peaks[pk].freq / (wdata.pitch*8)) + 1) / 2;
}
// adjust amplitude to compensate for fewer harmonics at higher pitch
- amplitude2 = (wdata.amplitude * wdata.pitch)/(100 << 11);
+// amplitude2 = (wdata.amplitude * wdata.pitch)/(100 << 11);
+ amplitude2 = (wdata.amplitude * (wdata.pitch >> 8) * wdata.amplitude_fmt)/(10000 << 3);
if(glottal_flag > 0)
{
@@ -1321,12 +1361,12 @@ int Wavegen()
for(h=1; h<=h_switch_sign; h++)
{
- total += ((int)(sin_tab[theta >> 5]) * harmspect[h]);
+ total += ((int)sin_tab[theta >> 5] * harmspect[h]);
theta += waveph;
}
while(h<=maxh)
{
- total -= ((int)(sin_tab[theta >> 5]) * harmspect[h]);
+ total -= ((int)sin_tab[theta >> 5] * harmspect[h]);
theta += waveph;
h++;
}
@@ -1351,17 +1391,20 @@ int Wavegen()
if(wdata.mix_wave_scale == 0)
{
// a 16 bit sample
- c = wdata.mix_wavefile[wdata.mix_wavefile_ix+1];
- sample = wdata.mix_wavefile[wdata.mix_wavefile_ix] + (c * 256);
+ c = wdata.mix_wavefile[wdata.mix_wavefile_ix+wdata.mix_wavefile_offset+1];
+ sample = wdata.mix_wavefile[wdata.mix_wavefile_ix+wdata.mix_wavefile_offset] + (c * 256);
wdata.mix_wavefile_ix += 2;
}
else
{
// a 8 bit sample, scaled
- sample = (signed char)wdata.mix_wavefile[wdata.mix_wavefile_ix++] * wdata.mix_wave_scale;
+ sample = (signed char)wdata.mix_wavefile[wdata.mix_wavefile_offset+wdata.mix_wavefile_ix++] * wdata.mix_wave_scale;
}
z2 = (sample * wdata.amplitude_v) >> 10;
z2 = (z2 * wdata.mix_wave_amp)/32;
+
+ if((wdata.mix_wavefile_ix + wdata.mix_wavefile_offset) >= wdata.mix_wavefile_max) // reached the end of available WAV data
+ wdata.mix_wavefile_offset -= (wdata.mix_wavefile_max*3)/4;
}
z1 = z2 + (((total>>8) * amplitude2) >> 13);
@@ -1406,11 +1449,12 @@ static int PlaySilence(int length, int resume)
static int n_samples;
int value=0;
- if(length == 0)
- return(0);
-
nsamples = 0;
samplecount = 0;
+ wavephase = 0x7fffffff;
+
+ if(length == 0)
+ return(0);
if(resume==0)
n_samples = length;
@@ -1507,14 +1551,38 @@ static int SetWithRange0(int value, int max)
}
+static void SetPitchFormants(void)
+{//===========================
+ int ix;
+ int factor = 256;
+ int pitch_value;
+
+ // adjust formants to give better results for a different voice pitch
+ if((pitch_value = embedded_value[EMBED_P]) > MAX_PITCH_VALUE)
+ pitch_value = MAX_PITCH_VALUE;
+
+ if(pitch_value > 50)
+ {
+ // only adjust if the pitch is higher than normal
+ factor = 256 + (25 * (pitch_value - 50))/50;
+ }
+
+ for(ix=0; ix<=5; ix++)
+ {
+ wvoice->freq[ix] = (wvoice->freq2[ix] * factor)/256;
+ }
+
+ factor = embedded_value[EMBED_T]*3;
+ wvoice->height[0] = (wvoice->height2[0] * (256 - factor*2))/256;
+ wvoice->height[1] = (wvoice->height2[1] * (256 - factor))/256;
+}
+
+
void SetEmbedded(int control, int value)
{//=====================================
// there was an embedded command in the text at this point
int sign=0;
int command;
- int ix;
- int factor;
- int pitch_value;
command = control & 0x1f;
if((control & 0x60) == 0x60)
@@ -1537,25 +1605,14 @@ void SetEmbedded(int control, int value)
case EMBED_T:
WavegenSetEcho(); // and drop through to case P
case EMBED_P:
- // adjust formants to give better results for a different voice pitch
- if((pitch_value = embedded_value[EMBED_P]) > MAX_PITCH_VALUE)
- pitch_value = MAX_PITCH_VALUE;
-
- factor = 256 + (25 * (pitch_value - 50))/50;
- for(ix=0; ix<=5; ix++)
- {
- wvoice->freq[ix] = (wvoice->freq2[ix] * factor)/256;
- }
- factor = embedded_value[EMBED_T]*3;
- wvoice->height[0] = (wvoice->height2[0] * (256 - factor*2))/256;
- wvoice->height[1] = (wvoice->height2[1] * (256 - factor))/256;
+ SetPitchFormants();
break;
case EMBED_A: // amplitude
general_amplitude = GetAmplitude();
break;
- case EMBED_F: // emphasiis
+ case EMBED_F: // emphasis
general_amplitude = GetAmplitude();
break;
@@ -1585,6 +1642,9 @@ void WavegenSetVoice(voice_t *v)
option_harmonic1 = 6;
}
WavegenSetEcho();
+ SetPitchFormants();
+ MarkerEvent(espeakEVENT_SAMPLERATE, 0, wvoice->samplerate, 0, out_ptr);
+// WVoiceChanged(wvoice);
}
@@ -1629,12 +1689,12 @@ void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pit
// compensate for change in pitch when the range is narrowed or widened
base -= (range - voice->pitch_range)*18;
- *pitch_base = base + (pitch1 * range);
- *pitch_range = base + (pitch2 * range) - *pitch_base;
+ *pitch_base = base + (pitch1 * range)/2;
+ *pitch_range = base + (pitch2 * range)/2 - *pitch_base;
}
-void SetPitch(int length, unsigned char *env, int pitch1, int pitch2)
+static void SetPitch(int length, unsigned char *env, int pitch1, int pitch2)
{//==================================================================
// length in samples
@@ -1671,7 +1731,7 @@ if(option_log_frames)
-void SetSynth(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v)
+static void SetSynth(int length, int modn, frame_t *fr1, frame_t *fr2, voice_t *v)
{//========================================================================
int ix;
DOUBLEX next;
@@ -1692,7 +1752,7 @@ if(option_log_frames)
fprintf(f_log,"%3dmS %3d %3d %4d %4d (%3d %3d %3d %3d) to %3d %3d %4d %4d (%3d %3d %3d %3d)\n",length*1000/samplerate,
fr1->ffreq[0],fr1->ffreq[1],fr1->ffreq[2],fr1->ffreq[3], fr1->fheight[0],fr1->fheight[1],fr1->fheight[2],fr1->fheight[3],
fr2->ffreq[0],fr2->ffreq[1],fr2->ffreq[2],fr2->ffreq[3], fr2->fheight[0],fr2->fheight[1],fr2->fheight[2],fr2->fheight[3] );
-
+
fclose(f_log);
f_log=NULL;
}
@@ -1751,27 +1811,27 @@ if(option_log_frames)
if(ix < 7)
{
peaks[ix].freq1 = (fr1->ffreq[ix] * v->freq[ix] + v->freqadd[ix]*256) << 8;
- peaks[ix].freq = (int)(peaks[ix].freq1);
+ peaks[ix].freq = (int)peaks[ix].freq1;
next = (fr2->ffreq[ix] * v->freq[ix] + v->freqadd[ix]*256) << 8;
peaks[ix].freq_inc = ((next - peaks[ix].freq1) * (STEPSIZE/4)) / length4; // lower headroom for fixed point math
}
peaks[ix].height1 = (fr1->fheight[ix] * v->height[ix]) << 6;
- peaks[ix].height = (int)(peaks[ix].height1);
+ peaks[ix].height = (int)peaks[ix].height1;
next = (fr2->fheight[ix] * v->height[ix]) << 6;
peaks[ix].height_inc = ((next - peaks[ix].height1) * STEPSIZE) / length2;
- if(ix <= wvoice->n_harmonic_peaks)
+ if((ix <= 5) && (ix <= wvoice->n_harmonic_peaks))
{
peaks[ix].left1 = (fr1->fwidth[ix] * v->width[ix]) << 10;
- peaks[ix].left = int(peaks[ix].left1);
+ peaks[ix].left = (int)peaks[ix].left1;
next = (fr2->fwidth[ix] * v->width[ix]) << 10;
peaks[ix].left_inc = ((next - peaks[ix].left1) * STEPSIZE) / length2;
if(ix < 3)
{
peaks[ix].right1 = (fr1->fright[ix] * v->width[ix]) << 10;
- peaks[ix].right = int(peaks[ix].right1);
+ peaks[ix].right = (int)peaks[ix].right1;
next = (fr2->fright[ix] * v->width[ix]) << 10;
peaks[ix].right_inc = ((next - peaks[ix].right1) * STEPSIZE) / length2;
}
@@ -1806,24 +1866,19 @@ void Write4Bytes(FILE *f, int value)
-
-int WavegenFill(int fill_zeros)
+static int WavegenFill2(int fill_zeros)
{//============================
// Pick up next wavegen commands from the queue
// return: 0 output buffer has been filled
// return: 1 input command queue is now empty
- long *q;
+ long64 *q;
int length;
int result;
+ int marker_type;
static int resume=0;
static int echo_complete=0;
-#ifdef TEST_MBROLA
- if(mbrola_name[0] != 0)
- return(MbrolaFill(fill_zeros));
-#endif
-
while(out_ptr < out_end)
{
if(WcmdqUsed() <= 0)
@@ -1848,7 +1903,7 @@ int WavegenFill(int fill_zeros)
q = wcmdq[wcmdq_head];
length = q[1];
- switch(q[0])
+ switch(q[0] & 0xff)
{
case WCMD_PITCH:
SetPitch(length,(unsigned char *)q[2],q[3] >> 16,q[3] & 0xffff);
@@ -1860,14 +1915,19 @@ int WavegenFill(int fill_zeros)
echo_complete -= length;
}
wdata.n_mix_wavefile = 0;
- wdata.prev_was_synth = 0;
+ wdata.amplitude_fmt = 100;
+#ifdef INCLUDE_KLATT
+ KlattReset(1);
+#endif
result = PlaySilence(length,resume);
break;
case WCMD_WAVE:
echo_complete = echo_length;
wdata.n_mix_wavefile = 0;
- wdata.prev_was_synth = 0;
+#ifdef INCLUDE_KLATT
+ KlattReset(1);
+#endif
result = PlayWave(length,resume,(unsigned char*)q[2], q[3] & 0xff, q[3] >> 8);
break;
@@ -1875,11 +1935,15 @@ int WavegenFill(int fill_zeros)
// wave file to be played at the same time as synthesis
wdata.mix_wave_amp = q[3] >> 8;
wdata.mix_wave_scale = q[3] & 0xff;
+ wdata.n_mix_wavefile = (length & 0xffff);
+ wdata.mix_wavefile_max = (length >> 16) & 0xffff;
if(wdata.mix_wave_scale == 0)
- wdata.n_mix_wavefile = length*2;
- else
- wdata.n_mix_wavefile = length;
+ {
+ wdata.n_mix_wavefile *= 2;
+ wdata.mix_wavefile_max *= 2;
+ }
wdata.mix_wavefile_ix = 0;
+ wdata.mix_wavefile_offset = 0;
wdata.mix_wavefile = (unsigned char *)q[2];
break;
@@ -1900,13 +1964,14 @@ int WavegenFill(int fill_zeros)
#endif
case WCMD_MARKER:
- MarkerEvent(q[1],q[2],q[3],out_ptr);
+ marker_type = q[0] >> 8;
+ MarkerEvent(marker_type, q[1],q[2],q[3],out_ptr);
#ifdef LOG_FRAMES
- LogMarker(q[1],q[3]);
+ LogMarker(marker_type, q[2], q[3]);
#endif
- if(q[1] == 1)
+ if(marker_type == 1) // word marker
{
- current_source_index = q[2] & 0xffffff;
+ current_source_index = q[1] & 0xffffff;
}
break;
@@ -1915,13 +1980,28 @@ int WavegenFill(int fill_zeros)
break;
case WCMD_VOICE:
- WavegenSetVoice((voice_t *)q[1]);
- free((voice_t *)q[1]);
+ WavegenSetVoice((voice_t *)q[2]);
+ free((voice_t *)q[2]);
break;
case WCMD_EMBEDDED:
SetEmbedded(q[1],q[2]);
break;
+
+ case WCMD_MBROLA_DATA:
+ result = MbrolaFill(length, resume, (general_amplitude * wvoice->voicing)/64);
+ break;
+
+ case WCMD_FMT_AMPLITUDE:
+ if((wdata.amplitude_fmt = q[1]) == 0)
+ wdata.amplitude_fmt = 100; // percentage, but value=0 means 100%
+ break;
+
+#ifdef INCLUDE_SONIC
+ case WCMD_SONIC_SPEED:
+ sonicSpeed = (double)q[1] / 1024;
+ break;
+#endif
}
if(result==0)
@@ -1936,6 +2016,64 @@ int WavegenFill(int fill_zeros)
}
return(0);
-} // end of WavegenFill
+} // end of WavegenFill2
+#ifdef INCLUDE_SONIC
+/* Speed up the audio samples with libsonic. */
+static int SpeedUp(short *outbuf, int length_in, int length_out, int end_of_text)
+{//==============================================================================
+ if(length_in >0)
+ {
+ if(sonicSpeedupStream == NULL)
+ {
+ sonicSpeedupStream = sonicCreateStream(22050, 1);
+ }
+ if(sonicGetSpeed(sonicSpeedupStream) != sonicSpeed)
+ {
+ sonicSetSpeed(sonicSpeedupStream, sonicSpeed);
+ }
+
+ sonicWriteShortToStream(sonicSpeedupStream, outbuf, length_in);
+ }
+
+ if(sonicSpeedupStream == NULL)
+ return(0);
+
+ if(end_of_text)
+ {
+ sonicFlushStream(sonicSpeedupStream);
+ }
+ return sonicReadShortFromStream(sonicSpeedupStream, outbuf, length_out);
+} // end of SpeedUp
+#endif
+
+
+/* Call WavegenFill2, and then speed up the output samples. */
+int WavegenFill(int fill_zeros)
+{//============================
+ int finished;
+ unsigned char *p_start;
+
+ p_start = out_ptr;
+
+ // fill_zeros is ignored. It is now done in the portaudio callback
+ finished = WavegenFill2(0);
+
+#ifdef INCLUDE_SONIC
+ if(sonicSpeed > 1.0)
+ {
+ int length;
+ int max_length;
+
+ max_length = (out_end - p_start);
+ length = 2*SpeedUp((short *)p_start, (out_ptr-p_start)/2, max_length/2, finished);
+ out_ptr = p_start + length;
+
+ if(length >= max_length)
+ finished = 0; // there may be more data to flush
+ }
+#endif
+ return finished;
+} // end of WavegenFill
+
diff --git a/navit/support/espeak/wavegen.h b/navit/support/espeak/wavegen.h
new file mode 100644
index 000000000..67b816c6d
--- /dev/null
+++ b/navit/support/espeak/wavegen.h
@@ -0,0 +1,21 @@
+/***************************************************************************
+ * Copyright (C) 2005 to 2010 by Jonathan Duddington *
+ * email: jonsd@users.sourceforge.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see: *
+ * <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range);
+int GetAmplitude(void);