Click here to Skip to main content
15,896,453 members
Articles / Mobile Apps / iPhone

ESpeakEngine - Objective-C speech synthesizer

Rate me:
Please Sign up or sign in to vote.
4.80/5 (3 votes)
23 Jan 2012BSD2 min read 75.3K   3K   22  
ESpeakEngine - Objective-C speech synthesizer
  • ESpeakTest.zip
    • __MACOSX
    • ESpeakTest
      • .DS_Store
      • .git
        • branches
        • COMMIT_EDITMSG
        • config
        • description
        • HEAD
        • hooks
          • applypatch-msg.sample
          • commit-msg.sample
          • post-commit.sample
          • post-receive.sample
          • post-update.sample
          • pre-applypatch.sample
          • pre-commit.sample
          • prepare-commit-msg.sample
          • pre-rebase.sample
          • update.sample
        • index
        • info
          • exclude
        • logs
          • HEAD
          • refs
            • heads
              • master
        • objects
          • 00
            • e88867c94b8f6728422a4ad099a53a9f392cb7
          • 01
            • 477be5465c04d4cfd46ab95003f17445731d74
          • 02
            • 37597b848a1890bb30ba0ff4102f8107cafdcb
            • 6363f6a3bb9fe78b03b09f4d0fdce472b95e20
            • 69a98a29eb1baf79dd1f6f2bbc9370b9c4c9cb
            • 78ea2136d97866104e3789d7950d39632b5046
          • 03
            • 7b0f820ace631b85715dfb13cc21fe26daa44d
            • dac4f6baff6f5a2b06f5a68b6daa265c16e0a2
            • edde41daf54530ac1fe426e9349a5544b48ccc
          • 05
            • b2d0d21b687b4190600f6ea0206972c960dd1c
          • 06
            • 23379b22e1417ed806ff4c48337afbe9457cdf
          • 0c
            • 2d13a65548266d9c8a6758599c9a86faf53b28
          • 0d
            • 51695daffab058d065e942018f5c56067942fe
            • 6fa2a91273ee3271fb1d3202d7f6ad86379ef7
          • 10
            • e91b22ea1e6c67962cd858e8d03de74e7abf6f
          • 11
            • 8095eb5859b9c2ee14be00adeb87ee66eaec94
          • 12
            • 83826f9250bed7326aed2542a0e3c5b47edbec
            • ce1096a4ef6c4538a43ebd951dcaf2b624b717
          • 13
            • 664a34fe6a3b585603e00df15b17bcef3591e0
            • 93b3551c88468dccede7550726aae27a931555
          • 14
            • 1160885f6955ee4855933ce984bc117649d220
            • 417c1fdfdb3af16ef717d90a078f5c9b8468c3
          • 15
            • 96e3c77205e1688ad5968359106b5ea92d322a
          • 17
            • 004519410f433308228397abc30d2b69e55b26
          • 19
            • 3700525eaa0aa02256a4211c820007bb005995
            • da34a5b9b6e585ece12d4a6ec24341fa98f50c
            • fd69d3bec6857b0d4b29f4f26c03ac0dc727e3
          • 1a
            • 61238c5c772724cb12490eb555630bcc0e7870
            • 9e53b6e584bd1e8245f3fc69751211b7d4e86a
          • 1c
            • 2992dd5455746aef85905cc0293031e3e9ceaa
            • a6f6abadb2e86be2baf2f823a27d16c43d2528
          • 1e
            • 9a757f891fd7ece2ff350dc9225837cb7e70f7
          • 1f
            • 2eb929019174a9b4c2654c1398ed26aca50e2c
          • 20
            • 5e59c21f973ea13de6deab139efee2f171027a
          • 21
            • 4d6f9d708a7691c1abf13b0801ee9a54a44ae6
          • 22
            • a3b80f6954fd663b59f66210e7b658c5db3a3f
            • a95d18a3ead5d0274abab7986121dccafc1842
          • 23
            • 8c69126e18387f9c2b3d059db6d7df1cd91a6f
          • 25
            • 55d5d8bec5ee0524034d96ea5aa95eeb1a90dd
            • 5fe35ca3bf7c8743abf0f95518aae822aac4f0
            • bfdbf47ea384bd8118ed197a8842cb2104eb2a
            • d78c05ca841d2019f204184927282dc69ebd61
          • 28
            • 59e093f75f1f88ca412e0bde9345afc01f15ac
          • 29
            • 91e99d136fe8e13f7c0c99eaa27c8ca397186d
          • 2a
            • 736d4d3f3c3ffe36bea620d6c28b672efc5867
            • fb1adc71b7f4e90bd02355dd8cdb75e8d23870
          • 2e
            • 1534872410e5819a69396cdbb57225baa3a888
            • 9dd1f58881b69e931f3b5a73fef461eb9b838b
          • 30
            • 441d7e239ccd9169d15b04efc59be7a158a4a5
            • dea89200864b8e84dc0fec67850491d460a78d
          • 31
            • 02c9a1a9a5db99cadf90502de610f69c994913
            • bd479a3d5873c8a970de821a84d2addc864839
          • 32
            • 49dd4f8012e94af03784f043478ebab38fd0b7
          • 33
            • 00c360065de4a18e093dd02df3f6419b656f74
            • 430a22c186f11e5cf838e81dacb386a7e4c8d0
            • 8b8c450f3027ea5d3751fc1e0b4bf969cc1afc
          • 35
            • a4a3fc517a71ec8bad8f9d0c71bd9da960c494
          • 36
            • 665314753a98e11162485805608fe1aca7bc1e
            • 669d3f0592eaeb8465b81341b486a4c9c1eb11
            • a4bff6503dfb82fcdc7a160cc36f4cdd9e333d
          • 3a
            • f2aa1af75587b7a29685fa2dbbebbd2b3592c2
          • 3b
            • 105a7fb0f9b3bb28a87a7212dfcef08daf53c0
          • 3d
            • d75260a68d0fb7b88fe70d0e3e73c916b963e5
          • 3e
            • 520ec5c8b88db8f95f6a230273278fd39bfbd6
          • 41
            • 22d96b39bf2bc1025a1c57ef01b8b03936773d
          • 42
            • dbdc5586bc89d681d23851f4a5aafd79b414cb
            • de58882d0c80e94597b575893afc8e99bd431e
          • 46
            • 07dd079dd2748f8e9cde034268e6cd68278644
            • 317618d912082d6070e4e3972809824bd35395
          • 47
            • 7b28ff8f86a3158a71c4934fbd3a2456717d7a
          • 4b
            • 6a9ae550599ea85d77fea7c0f71b4d5aaf1ba8
          • 4c
            • 6239268d21312d311a504ad0d8aeb0e4f1030b
          • 4d
            • ec15997c4e00c7a764156687431ac8c47676a5
          • 4e
            • 2b9d23e84059b93883e1a0c0f7a859a23b087e
          • 4f
            • 1904e5164410f93689beb55d46901b214271fe
            • 8f5e88f436d478b126c5c4eccf3568e398c26f
            • e4188e53b10cc21b50c3bf47e9ef3b2fc4c641
          • 50
            • 1b5a4a8620d5bd9a545c8941cdcbd565fca1c6
          • 52
            • 1be164ce6c87e1d6df58fd82ab160c8f6255db
            • 692c38546eb82aee2a7550c93798f70f02dc9a
            • c5ac93561331143a9caea14d6c0f008216b4e9
          • 53
            • 6957cb8fe03bde580784e6f97537ec3444e9c3
            • 7beb3ba82da8af147f028685e61fc839cad713
            • c2a70482993f53d6df321687b5cc4d9e95abc1
            • cb31446e077a5f159c831e126a0e3f9a2d0ed2
          • 56
            • 9f9d05432267a13b75ca2562beb85e1e1c17db
          • 58
            • 1cd883fed6aa3b84b580a543b6ec8998f4d327
          • 5a
            • 24e11ad4dc2842c79033ad323f02e2e6c2f566
            • 85640ee385afef9b9dc9b7d2889d47b81aa1bd
          • 5c
            • 3583da4700ab6982766d187d1195b37f9a3fb5
          • 5e
            • bb6a35716f489f249db8bb0e9df7dde150eba4
          • 5f
            • 3297d3a2009a6051a8ebc606bc674056ea03fc
          • 61
            • 124ff298a7d392b816cb1a71095ed1ec8ffe6b
          • 65
            • 3c3f5c4a2d2e44b8a188b88b64278085ebde27
          • 68
            • 1cad9627cb3af687a30507f05114c89ef9340f
          • 69
            • cceefb779a5236074db6871d7523d92f8a709f
          • 6a
            • 8d5efd87553a3f0e977636c6b819cddf3a99e7
            • cadba6aea97cd920745428a1c4ccd998581cc7
          • 6c
            • 65e3c6851f204d9c4cd9b616b46a9ff425b3c2
          • 6d
            • 826477b6ee1ecb3e502ffe3c26387d1bce3961
          • 6e
            • 11c93121ab5d535e4f2d50253ee4a527694a9f
          • 71
            • 99341c34f93f5fa5219ff479e82edaee5d7936
            • ecab7197ec9646efdae05bb02b465f5b5e361a
          • 73
            • ac62a4ab12374bbf6f72539b2e104d10d7d394
          • 74
            • 00c07a5c17fbbead0d252a22f1fcdb6e5f15c4
            • 0601d129aa08fd59be839301c923b684361dbe
          • 79
            • 2d8a9f9ef248c4358c36000722ba0c53a76497
          • 7c
            • bdab338114c51e83e0b54c67280b91872211d5
          • 7d
            • 276eb2b779d73c46d8ed97e4be287bec96c828
          • 7e
            • 6c16a2c28e97392d20d4f4c243ecd6f6f40a91
            • f93a5edd61fddc97d982242d7654e5ab07a09e
          • 7f
            • 4631899e208f50b855ad579b726eadea70f67d
            • fccbe6dd68c968e78da5b6265a13c62c1fc639
          • 82
            • 2c9a312addee71797811c17690f2be8746bea4
            • 98f98722e5f5f405e9631eb4a5064d87114424
          • 83
            • 71a46410d32c3f000db4c7b11254f48a3d6055
          • 84
            • 79e658ebe74cdd0b9525a41dededf8b9839858
            • ccc3a6d4681e19dbe6b982ddcee2e760944d55
          • 85
            • ebb03e3089c5055e4f76d272866738a90a7842
          • 88
            • 1634035cad7fbc213a9def0b5e5ef7f15dffa2
            • 48d6820e826b907349234a642535725247f837
          • 89
            • 28f0ff69aa677f7c5f96053ca70589552e17b0
            • e6c82914aa9457a644d5a498fa643f98b9ade3
          • 8f
            • c65d4bab0f132f34e70868a961188deabc55dc
            • d4a63a1a24c8b25eab5ed28c135457a8332e6b
          • 92
            • 3d517415d489cc9b3f91638c14264dd0df55a3
            • a1582817dc2f8256db5a02bed320a05f6e5e43
          • 95
            • 8799c61770d05ba341183cd2d6a107a1ec093c
          • 96
            • 35ac150af1804b398d67cf4703d718a16806a7
          • 97
            • 8ec49ed00c46862d89580efe68f7efd98c93c3
            • a337d34bb9ab89812b5e79c3bc2bb784d48953
          • 98
            • 9f9eab7b5ee98f4b6acf35fe8b4ef86db3a62e
          • 9b
            • 06e0bd24aa4658ed8009be3e2fe7e32ccee54e
            • 280bf8bc106ca904c9b33a90d0822c4c9b03fd
            • 2d891f12030afadc1e737914a1759ab59ef01f
            • a872a49ab896e3d7c6203bedfd2502d8cb6521
            • c60c7cbd38db3307551ae17ef2a8a5d623b3a0
          • 9d
            • e1630d90e22bf6df53a0093c212e9e1d7da9b6
            • eba8432350a07d0ab15189bb124d48b836fb62
          • 9e
            • 9c4e7476f3dce3b6808b0c8ea917a4d8503d7f
          • a3
            • 72fb170d6218ccc7298335f1ea55ff674fb3cc
          • a5
            • 4cb02cd26c7ecc2bd08debb1cfe85c3546c039
            • 504505ef08ce6040091e48eb5c1653a34574b0
          • a6
            • 56d2c7f2e9509fef687b3174282f6ec9b61498
            • e0f46bb57f876255cdb5767729aefccbe3a33e
          • a7
            • a8223dbda4d4cd47ae8796ad2be9bc70e46754
          • a9
            • 09e1af21f9dc9dccf095d02920266ea11fe7f5
            • 0c7b72565da6e015014e343f10ae50197587c9
          • aa
            • 80edaad05f0f16c6195e72a0130d803b58bcbc
          • ae
            • 76a4c4deb77d53e7ee512c3e85f45ba802dd08
            • 9247d41055f6721d3500645539ee00ae29d7e4
            • a3d895c09d5eae5411aec5fb5ecbe82451bd50
          • b0
            • d4979c857d151ef5cd27248926fb112c0c3cee
          • b1
            • a874be65ad7df189c933f4c576475f29d15f67
          • b2
            • 7a8114a03ed27348a40f37428107e02f67acc8
            • fd9d084c6df70da314916ee674421872f9ef50
          • b3
            • 2b6a6660b12a14fc5e8749eff9ba696b7f6061
          • b4
            • 8b1788b979853cdd57bb6cec6b9cc4dd7925bc
          • b7
            • 39a86e488eeec6f5693b7ee2590f84d6c95191
          • b8
            • 519559d3db834193518d4f4c4fed777734c8ee
            • 6f59306a576f4548ff3bd62e344570cc538932
            • f782946f4728a629eda8038e82dd165dadee1d
          • ba
            • 7c42cc48ae095ccfffeb03cf36d519d7cbe5a3
          • bc
            • bb2a0058b7866064b779ae246244c5342a0ccb
          • bd
            • 336a9884877889e2f85ee1849f8a068d2c6279
          • be
            • 1b6246a01d3d9c01dac0f888454369e0b32892
          • c0
            • a5475ece89c3274e62f9132560ec8afdd569d4
          • c2
            • 34f46877a761d5c98fda2cbbeb172dc2efc81a
            • 76bec0dd56b68a8e0ed0062b38dd149422b45d
          • c3
            • 2db3968df0779b3c3a47899085625a7b86339b
          • c6
            • 32e263c2a639311be4ca1512aadee97a91bc9a
          • cb
            • c4fb3a5d3dcc07f26b5ca02eb53a52b897aa80
          • cd
            • 02abedb89d8975a792d73de536d2ce82260553
          • ce
            • 800f70be34d9fba11f7d09b0c329b3f259507b
          • cf
            • 584b7dd3f005be3cffb47d27f1bfe980a6e53b
            • f13bac9dd46f2c2a2fb66db64ce78caffd5b5a
          • d0
            • 2b035cae9b12c66a5e0d5ae9cf57433041ed40
            • 864f3d6209ad6f3e8ab51dc880836e67c96fc5
            • b729579de2537a63cebdb809e8bdfb5a897a33
          • d2
            • 5865608d7d973e77c77d13bbdbe3ae454d864d
          • d3
            • d7720074a2fc7e3bfbc18cc1bacad0f9ea61d2
          • d5
            • 06e7f9f85667d054794f808f9098b2a1625372
            • 9fe79529970f627e83857f9bd516f5474bf685
          • d6
            • 737d787752a799349095b49cf81024dfd00fb6
            • 811d3ae4f710d3eb8a16c690cd42bebc4faf4e
          • d7
            • 59cfdf018f8814d995588d176dadde0820b5ca
          • d8
            • ecd252c04bc00a6dcc6d0b84d64285726b65d4
            • f35efaa2dc2293277365d811c9b6222cfe59b8
          • da
            • c1e4d0643b01e99052f575b5f6add2e10a2143
            • ef5160c9c1500d3a9f6b0eadf434ef702a009f
          • db
            • 63d6ca3d87629e693327a2ba4a719e79847b6c
            • de212341a0b131224b3e123f91d154348070ba
          • dc
            • 51396ce24aad46d1c761e2c96f8aee68d4b622
          • de
            • 4786c94cfa90c84982ca06a2a9623035993dfc
          • df
            • 70f4387ca97d30c66d7dc87d5abca9b0ffa820
            • 7fa77bc23477530674f05f4f619803983d5eb0
          • e0
            • 783ec3b1e38ce0f77b5b018d9b8641a7b924c3
            • ea6d6394ab8409463adfeb753f1632ef09a47f
          • e4
            • 16c6dc5e784243ad8d33000139178fa178de87
            • 1d3105c048e789fe605c780efe2881df833ea8
          • e7
            • 178e4d5666a3b75aa48de33b966f0590665a8e
          • e8
            • 5978c76abd7430436356cd2a126018642d6a2d
          • ea
            • dd707322e08d90e3a2208aef9060706c340799
          • eb
            • d92ffb3ec9499292d1faa7ca60adcc5edac74f
          • ed
            • 05f4240da98a569e3c9f9a5b9e2301d7caa79e
          • ee
            • 3bd233e2bc458aab63eb36f613f853f72e6bf7
          • f2
            • 033dc11fee0a1db4a2cae358c1a808149a5aa2
            • 130ba4f980783f8605eb50387ffc2e54f66b3c
          • f3
            • 10f868efea6408fc49fd4e6d6c293db2098114
            • e97b52355ea6f5384790bb3cda677222be900c
          • f4
            • 3ef84fa6caf08bc6e9e442e15d71eaa8c6962a
            • ede3296c4fb294a58715e61ecef26f74ed898a
          • f5
            • 0e89c905a3e8dd519f415585283757fc85d2d6
          • f9
            • 624b31623b500b57b74e9765ac7a2d9f039c65
          • fa
            • 4eece0b1cc7dababed3d09d560f38e00ea8755
          • fc
            • 377156b487a559efb384ec2b6e551d6c2085c6
            • 60f41672c8fb6fc69c1548552988f046a2aa14
          • fe
            • 7c4d8d002499fbaff530adfaa0543d626536dd
          • ff
            • a94206372e46914dcadeb10c212a9674daf62e
          • info
          • pack
        • refs
          • heads
            • master
          • tags
      • ESpeakTest.xcodeproj
        • project.pbxproj
        • project.xcworkspace
          • contents.xcworkspacedata
          • xcuserdata
            • jozefbozek.xcuserdatad
              • UserInterfaceState.xcuserstate
        • xcuserdata
      • ESpeakTest
      • ESpeakTestTests
  • eSpeak_1.0.zip
/***************************************************************************
 *   Copyright (C) 2005 to 2007 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, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

#include "StdAfx.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wctype.h>

#include "speak_lib.h"
#include "speech.h"
#include "phoneme.h"
#include "synthesize.h"
#include "voice.h"
#include "translate.h"


/* Note this module is mostly old code that needs to be rewritten to
   provide a more flexible intonation system.
*/

// bits in SYLLABLE.flags
#define SYL_RISE        1
#define SYL_EMPHASIS    2
#define SYL_END_CLAUSE   4

typedef struct {
	char stress;
	char env;
	char flags;   //bit 0=pitch rising, bit1=emnphasized, bit2=end of clause
	char nextph_type;
	unsigned char pitch1;
	unsigned char pitch2;
} SYLLABLE;

static SYLLABLE *syllable_tab;


static int tone_pitch_env;    /* used to return pitch envelope */



/* Pitch data for tone types */
/*****************************/


#define    PITCHfall   0
#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] = {
 0xff, 0xfd, 0xfa, 0xf8, 0xf6, 0xf4, 0xf2, 0xf0, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0,
 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2, 0xc0,
 0xbe, 0xbc, 0xba, 0xb8, 0xb6, 0xb4, 0xb2, 0xb0, 0xae, 0xac, 0xaa, 0xa8, 0xa6, 0xa4, 0xa2, 0xa0,
 0x9e, 0x9c, 0x9a, 0x98, 0x96, 0x94, 0x92, 0x90, 0x8e, 0x8c, 0x8a, 0x88, 0x86, 0x84, 0x82, 0x80,
 0x7e, 0x7c, 0x7a, 0x78, 0x76, 0x74, 0x72, 0x70, 0x6e, 0x6c, 0x6a, 0x68, 0x66, 0x64, 0x62, 0x60,
 0x5e, 0x5c, 0x5a, 0x58, 0x56, 0x54, 0x52, 0x50, 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x42, 0x40,
 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20,
 0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00 };

/*  1  rise */
unsigned char env_rise[128] = {
 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfd, 0xff };

unsigned char env_frise[128] = {
 0xff, 0xf4, 0xea, 0xe0, 0xd6, 0xcc, 0xc3, 0xba, 0xb1, 0xa8, 0x9f, 0x97, 0x8f, 0x87, 0x7f, 0x78,
 0x71, 0x6a, 0x63, 0x5c, 0x56, 0x50, 0x4a, 0x44, 0x3f, 0x39, 0x34, 0x2f, 0x2b, 0x26, 0x22, 0x1e,
 0x1a, 0x17, 0x13, 0x10, 0x0d, 0x0b, 0x08, 0x06, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x13, 0x15, 0x17,
 0x1a, 0x1d, 0x1f, 0x22, 0x25, 0x28, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3b, 0x3d, 0x40,
 0x42, 0x45, 0x47, 0x4a, 0x4c, 0x4f, 0x51, 0x54, 0x57, 0x5a, 0x5d, 0x5f, 0x62, 0x65, 0x68, 0x6b,
 0x6e, 0x71, 0x74, 0x78, 0x7b, 0x7e, 0x81, 0x85, 0x88, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa0,
 0xa4, 0xa8, 0xac, 0xaf, 0xb3, 0xb7, 0xbb, 0xbf, 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7, 0xdb, 0xe0 };

static unsigned char env_r_frise[128] = {
 0xcf, 0xcc, 0xc9, 0xc6, 0xc3, 0xc0, 0xbd, 0xb9, 0xb4, 0xb0, 0xab, 0xa7, 0xa2, 0x9c, 0x97, 0x92,
 0x8c, 0x86, 0x81, 0x7b, 0x75, 0x6f, 0x69, 0x63, 0x5d, 0x57, 0x50, 0x4a, 0x44, 0x3e, 0x38, 0x33,
 0x2d, 0x27, 0x22, 0x1c, 0x17, 0x12, 0x0d, 0x08, 0x04, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x0a, 0x0c, 0x0d, 0x0f, 0x12, 0x14, 0x16,
 0x19, 0x1b, 0x1e, 0x21, 0x24, 0x27, 0x2a, 0x2d, 0x30, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3f, 0x41,
 0x43, 0x46, 0x48, 0x4b, 0x4d, 0x50, 0x52, 0x55, 0x58, 0x5a, 0x5d, 0x60, 0x63, 0x66, 0x69, 0x6c,
 0x6f, 0x72, 0x75, 0x78, 0x7b, 0x7e, 0x81, 0x85, 0x88, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa0,
 0xa4, 0xa8, 0xac, 0xaf, 0xb3, 0xb7, 0xbb, 0xbf, 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7, 0xdb, 0xe0 };

static unsigned char env_frise2[128] = {
 0xff, 0xf9, 0xf4, 0xee, 0xe9, 0xe4, 0xdf, 0xda, 0xd5, 0xd0, 0xcb, 0xc6, 0xc1, 0xbd, 0xb8, 0xb3,
 0xaf, 0xaa, 0xa6, 0xa1, 0x9d, 0x99, 0x95, 0x90, 0x8c, 0x88, 0x84, 0x80, 0x7d, 0x79, 0x75, 0x71,
 0x6e, 0x6a, 0x67, 0x63, 0x60, 0x5d, 0x59, 0x56, 0x53, 0x50, 0x4d, 0x4a, 0x47, 0x44, 0x41, 0x3e,
 0x3c, 0x39, 0x37, 0x34, 0x32, 0x2f, 0x2d, 0x2b, 0x28, 0x26, 0x24, 0x22, 0x20, 0x1e, 0x1c, 0x1a,
 0x19, 0x17, 0x15, 0x14, 0x12, 0x11, 0x0f, 0x0e, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05,
 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x08,
 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x20 };

static unsigned char env_r_frise2[128] = {
 0xd0, 0xce, 0xcd, 0xcc, 0xca, 0xc8, 0xc7, 0xc5, 0xc3, 0xc1, 0xc0, 0xbd, 0xbb, 0xb8, 0xb5, 0xb3,
 0xb0, 0xad, 0xaa, 0xa7, 0xa3, 0xa0, 0x9d, 0x99, 0x96, 0x92, 0x8f, 0x8b, 0x87, 0x84, 0x80, 0x7c,
 0x78, 0x74, 0x70, 0x6d, 0x69, 0x65, 0x61, 0x5d, 0x59, 0x55, 0x51, 0x4d, 0x4a, 0x46, 0x42, 0x3e,
 0x3b, 0x37, 0x34, 0x31, 0x2f, 0x2d, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20, 0x1e, 0x1c, 0x1a, 0x19,
 0x17, 0x15, 0x14, 0x12, 0x11, 0x0f, 0x0e, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x05,
 0x04, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x06, 0x07, 0x08,
 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x20 };

static unsigned char env_risefall[128] = {
 0x98, 0x99, 0x99, 0x9a, 0x9c, 0x9d, 0x9f, 0xa1, 0xa4, 0xa7, 0xa9, 0xac, 0xb0, 0xb3, 0xb6, 0xba,
 0xbe, 0xc1, 0xc5, 0xc9, 0xcd, 0xd1, 0xd4, 0xd8, 0xdc, 0xdf, 0xe3, 0xe6, 0xea, 0xed, 0xf0, 0xf2,
 0xf5, 0xf7, 0xf9, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd,
 0xfb, 0xfa, 0xf8, 0xf6, 0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe4, 0xe0, 0xdd, 0xda, 0xd7, 0xd3,
 0xd0, 0xcc, 0xc8, 0xc4, 0xc0, 0xbc, 0xb8, 0xb4, 0xb0, 0xac, 0xa7, 0xa3, 0x9f, 0x9a, 0x96, 0x91,
 0x8d, 0x88, 0x84, 0x7f, 0x7b, 0x76, 0x72, 0x6d, 0x69, 0x65, 0x60, 0x5c, 0x58, 0x54, 0x50, 0x4c,
 0x48, 0x44, 0x40, 0x3c, 0x39, 0x35, 0x32, 0x2f, 0x2b, 0x28, 0x26, 0x23, 0x20, 0x1d, 0x1a, 0x17,
 0x15, 0x12, 0x0f, 0x0d, 0x0a, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };

static unsigned char env_rise2[128] = {
 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x06, 0x06,
 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1d, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2b,
 0x2d, 0x2f, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x47, 0x49, 0x4b,
 0x4e, 0x50, 0x52, 0x55, 0x57, 0x5a, 0x5d, 0x5f, 0x62, 0x65, 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x76,
 0x79, 0x7c, 0x7f, 0x82, 0x86, 0x89, 0x8c, 0x90, 0x93, 0x96, 0x9a, 0x9d, 0xa0, 0xa3, 0xa6, 0xa9,
 0xac, 0xaf, 0xb2, 0xb5, 0xb8, 0xbb, 0xbe, 0xc1, 0xc4, 0xc7, 0xca, 0xcd, 0xd0, 0xd3, 0xd6, 0xd9,
 0xdc, 0xdf, 0xe2, 0xe4, 0xe7, 0xe9, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfb, 0xfd };

static unsigned char env_fall2[128] = {
 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6,
 0xf5, 0xf4, 0xf4, 0xf3, 0xf3, 0xf2, 0xf2, 0xf1, 0xf0, 0xf0, 0xef, 0xee, 0xee, 0xed, 0xec, 0xeb,
 0xea, 0xea, 0xe9, 0xe8, 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xde, 0xdd, 0xdc, 0xdb,
 0xd9, 0xd8, 0xd6, 0xd5, 0xd3, 0xd2, 0xd0, 0xce, 0xcc, 0xcb, 0xc9, 0xc7, 0xc5, 0xc3, 0xc0, 0xbe,
 0xbc, 0xb9, 0xb7, 0xb5, 0xb2, 0xaf, 0xad, 0xaa, 0xa7, 0xa4, 0xa1, 0x9e, 0x9a, 0x97, 0x94, 0x90,
 0x8d, 0x89, 0x85, 0x81, 0x7d, 0x79, 0x75, 0x71, 0x6d, 0x68, 0x64, 0x61, 0x5e, 0x5b, 0x57, 0x54,
 0x51, 0x4d, 0x4a, 0x46, 0x43, 0x40, 0x3c, 0x39, 0x35, 0x32, 0x2e, 0x2a, 0x27, 0x23, 0x1f, 0x1c,
 0x18, 0x14, 0x11, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00 };

static unsigned char env_fallrise3[128] = {
 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfa, 0xf8, 0xf6, 0xf4, 0xf1, 0xee, 0xeb,
 0xe8, 0xe5, 0xe1, 0xde, 0xda, 0xd6, 0xd2, 0xcd, 0xc9, 0xc4, 0xbf, 0xba, 0xb6, 0xb0, 0xab, 0xa6,
 0xa1, 0x9c, 0x96, 0x91, 0x8b, 0x86, 0x80, 0x7b, 0x75, 0x6f, 0x6a, 0x64, 0x5f, 0x59, 0x54, 0x4f,
 0x49, 0x44, 0x3f, 0x3a, 0x35, 0x30, 0x2b, 0x26, 0x22, 0x1d, 0x19, 0x15, 0x11, 0x0d, 0x0a, 0x07,
 0x04, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x04, 0x05,
 0x07, 0x09, 0x0b, 0x0d, 0x10, 0x12, 0x15, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x29, 0x2d, 0x31, 0x35,
 0x3a, 0x3e, 0x43, 0x48, 0x4c, 0x51, 0x57, 0x5b, 0x5e, 0x62, 0x65, 0x68, 0x6b, 0x6e, 0x71, 0x74,
 0x76, 0x78, 0x7b, 0x7c, 0x7e, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x83, 0x83, 0x82, 0x81 };

static unsigned char env_fallrise4[128] = {
 0x72, 0x72, 0x71, 0x71, 0x70, 0x6f, 0x6d, 0x6c, 0x6a, 0x68, 0x66, 0x64, 0x61, 0x5f, 0x5c, 0x5a,
 0x57, 0x54, 0x51, 0x4e, 0x4b, 0x48, 0x45, 0x42, 0x3f, 0x3b, 0x38, 0x35, 0x32, 0x2f, 0x2c, 0x29,
 0x26, 0x23, 0x20, 0x1d, 0x1b, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06,
 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1b, 0x1d, 0x20,
 0x23, 0x26, 0x29, 0x2c, 0x2f, 0x33, 0x37, 0x3b, 0x3f, 0x43, 0x47, 0x4c, 0x51, 0x56, 0x5b, 0x60,
 0x65, 0x6a, 0x6f, 0x74, 0x79, 0x7f, 0x84, 0x89, 0x8f, 0x95, 0x9b, 0xa1, 0xa7, 0xad, 0xb3, 0xba,
 0xc0, 0xc7, 0xce, 0xd5, 0xdc, 0xe3, 0xea, 0xf1, 0xf5, 0xf7, 0xfa, 0xfc, 0xfd, 0xfe, 0xff, 0xff };

static unsigned char env_risefallrise[128] = {
 0x7f, 0x7f, 0x7f, 0x80, 0x81, 0x83, 0x84, 0x87, 0x89, 0x8c, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa1,
 0xa5, 0xaa, 0xae, 0xb2, 0xb7, 0xbb, 0xc0, 0xc5, 0xc9, 0xcd, 0xd2, 0xd6, 0xda, 0xde, 0xe2, 0xe6,
 0xea, 0xed, 0xf0, 0xf3, 0xf5, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xfe, 0xfd, 0xfc, 0xfb, 0xf9,
 0xf7, 0xf4, 0xf0, 0xec, 0xe7, 0xe2, 0xdc, 0xd5, 0xce, 0xc6, 0xbd, 0xb4, 0xa9, 0x9e, 0x92, 0x88,
 0x82, 0x7d, 0x77, 0x72, 0x6c, 0x66, 0x60, 0x5a, 0x54, 0x4e, 0x49, 0x42, 0x3c, 0x37, 0x32, 0x2d,
 0x28, 0x24, 0x1f, 0x1b, 0x18, 0x14, 0x11, 0x0e, 0x0c, 0x09, 0x07, 0x06, 0x05, 0x04, 0x04, 0x04,
 0x04, 0x05, 0x06, 0x08, 0x0a, 0x0d, 0x10, 0x14, 0x18, 0x1d, 0x23, 0x29, 0x2f, 0x37, 0x3e, 0x47,
 0x50, 0x5a, 0x64, 0x70, 0x7c, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x93, 0x93 };




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,

	env_fallrise3, env_fallrise3,
	env_fallrise4, env_fallrise4,
	env_fall2, env_fall2,
	env_rise2, env_rise2,
	env_risefallrise, env_risefallrise
 };



/* 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] = {9,9,16,16,16,23,55,32};

// 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
#define N_TONE_NUCLEUS_TABLE  13


typedef struct {
	unsigned char pre_start;
	unsigned char pre_end;

	unsigned char body_start;
	unsigned char body_end;

	int  *body_drops;
	unsigned char body_max_steps;
	char body_lower_u;

	unsigned char n_overflow;
	signed char *overflow;
} TONE_HEAD;


typedef struct {
	unsigned char pitch_env0;     /* pitch envelope, tonic syllable at end */
	unsigned char tonic_max0;
	unsigned char tonic_min0;

	unsigned char pitch_env1;     /*     followed by unstressed */
	unsigned char tonic_max1;
	unsigned char tonic_min1;

	short *backwards;

	unsigned char tail_start;
	unsigned char tail_end;
	unsigned char flags;
} TONE_NUCLEUS;

#define T_EMPH  1

static TONE_HEAD tone_head_table[N_TONE_HEAD_TABLE] = {
   {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,   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 */
unsigned char punctuation_to_tone[INTONATION_TYPES][PUNCT_INTONATIONS] = {
	{0,1,2,3,0,4},
	{0,1,2,3,0,4},
	{5,6,2,3,0,4},
	{5,7,1,3,0,4},
	{8,9,10,3,0,0},
	{8,8,10,3,0,0},
	{11,11,11,11,0,0},  // 6 test
	{12,12,12,12,0,0}
};


int n_tunes = 0;
TUNE *tunes = NULL;


#define SECONDARY  3
#define PRIMARY    4
#define PRIMARY_STRESSED 6
#define PRIMARY_LAST 7


static int  number_pre;
static int  number_body;
static int  number_tail;
static int  last_primary;
static int  tone_posn;
static int  tone_posn2;
static int  no_tonic;


static void count_pitch_vowels(int start, int end, int clause_end)
/****************************************************************/
{
	int  ix;
	int  stress;
	int  max_stress = 0;
	int  max_stress_posn = 0;  // last syllable ot the highest stress
	int  max_stress_posn2 = 0;  // penuntimate syllable of the highest stress

	number_pre = -1;    /* number of vowels before 1st primary stress */
	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 */

		if(stress >= max_stress)
		{
			if(stress > max_stress)
			{
				max_stress_posn2 = ix;
			}
			else
			{
				max_stress_posn2 = max_stress_posn;
			}
			max_stress_posn = ix;
			max_stress = stress;
		}
		if(stress >= PRIMARY)
		{
			if(number_pre < 0)
				number_pre = ix - start;

			last_primary = ix;
		}

	}

	if(number_pre < 0)
		number_pre = end;

	number_tail = end - max_stress_posn - 1;
	tone_posn = max_stress_posn;
	tone_posn2 = max_stress_posn2;

	if(no_tonic)
	{
		tone_posn = tone_posn2 = end;  // next position after the end of the truncated clause
	}
	else
	if(last_primary >= 0)
	{
		if(end == clause_end)
		{
			syllable_tab[last_primary].stress = PRIMARY_LAST;
		}
	}
	else
	{
		// no primary stress. Use the highest stress
		syllable_tab[tone_posn].stress = PRIMARY_LAST;
	}
}   /* end of count_pitch_vowels */




static int count_increments(int ix, int end_ix, int min_stress)
/*************************************************************/
/* Count number of primary stresses up to tonic syllable or body_reset */
{
	int  count = 0;
	int  stress;

	while(ix < end_ix)
	{
		stress = syllable_tab[ix++].stress;
		if(stress >= PRIMARY_LAST)
			break;

		if(stress >= min_stress)
			count++;
	}
	return(count);
}  /* end of count_increments */




static void set_pitch(SYLLABLE *syl, int base, int drop)
/******************************************************/
// Set the pitch of a vowel in syllable_tab
{
	int  pitch1, pitch2;
	int  flags = 0;

	if(base < 0)  base = 0;

	pitch2 = base;

	if(drop < 0)
	{
		flags = SYL_RISE;
		drop = -drop;
	}

	pitch1 = pitch2 + drop;
	if(pitch1 < 0)
		pitch1 = 0;

	if(pitch1 > 254) pitch1 = 254;
	if(pitch2 > 254) pitch2 = 254;

	syl->pitch1 = pitch1;
	syl->pitch2 = pitch2;
	syl->flags |= flags;
}   /* 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)
/**********************************************************************************************/
/* Calculate pitches until next RESET or tonic syllable, or end.
	Increment pitch if stress is >= min_stress.
	Used for tonic segment */
{
	int  stress;
	int  pitch=0;
	int  increment=0;
	int  n_primary=0;
	int  n_steps=0;
	int  initial;
	int  overflow=0;
	int  n_overflow;
	int  pitch_range;
	int  pitch_range_abs;
	int *drops;
	signed char *overflow_tab;
	SYLLABLE *syl;

	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)
	{
		initial =0;
		overflow = 0;
		n_overflow = 5;
		overflow_tab = continue_tab;
		increment = pitch_range / (th->body_max_steps -1);
	}
	else
	{
		n_overflow = th->n_overflow;
		overflow_tab = th->overflow;
		initial = 1;
	}

	while(ix < end_ix)
	{
		syl = &syllable_tab[ix];
		stress = syl->stress;

//		if(stress == PRIMARY_MARKED)
//			initial = 1;    // reset the intonation pattern

		if(initial || (stress >= min_stress))
		{
			// a primary stress

			if((initial) || (stress == 5))
			{
				initial = 0;
				overflow = 0;
				n_steps = n_primary = count_increments(ix,end_ix,min_stress);

				if(n_steps > th->body_max_steps)
					n_steps = th->body_max_steps;

				if(n_steps > 1)
				{
					increment = pitch_range / (n_steps -1);
				}
				else
					increment = 0;

				pitch = th->body_start << 8;
			}
			else
			{
				if(n_steps > 0)
					pitch += increment;
				else
				{
					pitch = (th->body_end << 8) + (pitch_range_abs * overflow_tab[overflow++])/64;
					if(overflow >= n_overflow)
					{
						overflow = 0;
						overflow_tab = th->overflow;
					}
				}
			}

			n_steps--;

			n_primary--;
			if((tn->backwards) && (n_primary < 2))
			{
					pitch = tn->backwards[n_primary] << 8;	
			}
		}

		if(stress >= PRIMARY)
		{
			syl->stress = PRIMARY_STRESSED;
			set_pitch(syl,(pitch >> 8),drops[stress]);
		}
		else
		if(stress >= SECONDARY)
		{
			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 >> 8) - th->body_lower_u, drops[stress]);
			else
				set_pitch(syl,(pitch >> 8),drops[stress]);
		}

		ix++;
	}
	return(ix);
}   /* end of calc_pitch_segment */



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

	int  ix;
	int  stress;
	int  pitch;
	int  increment;
	int  n_increments;
	int  drop;
	SYLLABLE *syl;

	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_pitch << 8;

	for(ix=start_ix; ix < end_ix; ix++)
	{
		syl = &syllable_tab[ix];
		stress = syl->stress;

		if(increment > 0)
		{
			set_pitch(syl,(pitch >> 8),-(increment >> 8));
			pitch += increment;
		}
		else
		{
			drop = -(increment >> 8);
			if(drop < min_drop[stress])
				drop = min_drop[stress];

			pitch += increment;

			if(drop > 18)
				drop = 18;
			set_pitch(syl, (pitch >> 8), drop);
		}
	}
}  // 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;
	int continuing = 0;

	if(start > 0)
		continuing = 1;

	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 */
	/*************************/

	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;
	TONE_HEAD *th;
	TONE_NUCLEUS *tn;
	int  drop;
	int continuing = 0;

	if(control == 0)
	{
		return(calc_pitches2(start, end, tune_number));
	}

	if(start > 0)
		continuing = 1;

	th = &tone_head_table[tune_number];
	tn = &tone_nucleus_table[tune_number];
	ix = start;

	/* vowels before the first primary stress */
	/******************************************/

	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 
	}
	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;
	}

	if(number_tail == 0)
	{
		tone_pitch_env = tn->pitch_env0;
		drop = tn->tonic_max0 - tn->tonic_min0;
		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, 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, tn->tail_start, tn->tail_end);

	return(tone_pitch_env);
}   /* end of calc_pitches */






static void CalcPitches_Tone(Translator *tr, int clause_tone)
{//==========================================================
//  clause_tone: 0=. 1=, 2=?, 3=! 4=none
	PHONEME_LIST *p;
	int  ix;
	int  count_stressed=0;
	int  final_stressed=0;

	int  tone_ph;
	int pause;
	int tone_promoted;
	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_LIST *prev_p;

	int  pitch_adjust = 0;     // pitch gradient through the clause - inital value
	int  pitch_decrement = 0;   //   decrease by this for each stressed syllable
	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++)
	{
		if((p->type == phVOWEL) && (p->stresslevel >= 4))
		{
			if(count_stressed == 0)
				final_stressed = ix;

			if(p->stresslevel >= 4)
			{
				final_stressed = ix;
				count_stressed++;
			}
		}
	}

	phoneme_list[final_stressed].stresslevel = 7;

	// language specific, changes to tones
	if(tr->translator_name == L('v','i'))
	{
		// LANG=vi
		p = &phoneme_list[final_stressed];
		if(p->tone_ph == 0)
			p->tone_ph = PhonemeCode('7');   // change default tone (tone 1) to falling tone at end of clause
	}


	pause = 1;
	tone_promoted = 0;

	prev_p = p = &phoneme_list[0];
	prev_tph = prevw_tph = phoneme_tab[phonPAUSE];

	// perform tone sandhi
	for(ix=0; ix<n_phoneme_list; ix++, p++)
	{
		if((p->type == phPAUSE) && (p->ph->std_length > 50))
		{
			pause = 1;  // there is a pause since the previous vowel
			prevw_tph = phoneme_tab[phonPAUSE];  // forget previous tone
		}

		if(p->newword)
		{
			prev_tph = phoneme_tab[phonPAUSE];  // forget across word boundaries
		}

		if(p->synthflags & SFLAG_SYLLABLE)
		{
			tone_ph = p->tone_ph;
			tph = phoneme_tab[tone_ph];

			// Mandarin
			if(tr->translator_name == L('z','h'))
			{
				if(tone_ph == 0)
				{
					if(pause || tone_promoted)
					{
						tone_ph = PhonemeCode2('5','5');  // no previous vowel, use tone 1
						tone_promoted = 1;
					}
					else
					{
						tone_ph = PhonemeCode2('1','1');  // default tone 5
					}

					p->tone_ph = tone_ph;
					tph = phoneme_tab[tone_ph];

				}
				else
				{
					tone_promoted = 0;
				}

				if(ix == final_stressed)
				{
					if((tph->mnemonic == 0x3535 ) || (tph->mnemonic == 0x3135))
					{
						// change sentence final tone 1 or 4 to stress 6, not 7
						phoneme_list[final_stressed].stresslevel = 6;
					}
				}

				if(prevw_tph->mnemonic == 0x343132)  // [214]
				{
					if(tph->mnemonic == 0x343132)   // [214]
						prev_p->tone_ph = PhonemeCode2('3','5');
					else
						prev_p->tone_ph = PhonemeCode2('2','1'); 
				}
				if((prev_tph->mnemonic == 0x3135)  && (tph->mnemonic == 0x3135))  //  [51] + [51]
				{
					prev_p->tone_ph = PhonemeCode2('5','3');
				}

				if(tph->mnemonic == 0x3131)  // [11] Tone 5
				{
					// tone 5, change its level depending on the previous tone (across word boundaries)
					if(prevw_tph->mnemonic == 0x3535)
						p->tone_ph = PhonemeCode2('2','2');
					if(prevw_tph->mnemonic == 0x3533)
						p->tone_ph = PhonemeCode2('3','3');
					if(prevw_tph->mnemonic == 0x343132)
						p->tone_ph = PhonemeCode2('4','4');

					// tone 5 is unstressed (shorter)
					p->stresslevel = 0;   // diminished stress
				}
			}

			prev_p = p;
			prev2_tph = prevw_tph;
			prevw_tph = prev_tph = tph;
			pause = 0;
		}
	}

	// convert tone numbers to pitch
	p = &phoneme_list[0];
	for(ix=0; ix<n_phoneme_list; ix++, p++)
	{
		if(p->synthflags & SFLAG_SYLLABLE)
		{
			tone_ph = p->tone_ph;

			if(p->stresslevel != 0)  // TEST, consider all syllables as stressed
			{
				if(ix == final_stressed)
				{
					// the last stressed syllable
					pitch_adjust = pitch_low;
				}
				else
				{
					pitch_adjust -= pitch_decrement;
					if(pitch_adjust <= pitch_low)
						pitch_adjust = pitch_high;
				}
			}

			if(tone_ph ==0)
			{
				tone_ph = phonDEFAULTTONE;  // no tone specified, use default tone 1
				p->tone_ph = tone_ph;
			}
			p->pitch1 = pitch_adjust + phoneme_tab[tone_ph]->start_type;
			p->pitch2 = pitch_adjust + phoneme_tab[tone_ph]->end_type;
		}
	}


}  // end of Translator::CalcPitches_Tone



void CalcPitches(Translator *tr, int clause_type)
{//==============================================
//  clause_type: 0=. 1=, 2=?, 3=! 4=none
	PHONEME_LIST *p;
	SYLLABLE *syl;
	int  ix;
	int  x;
	int  st_ix;
	int n_st;
	int  option;
	int  group_tone;
	int  group_tone_emph;
	int  group_tone_comma;
	int ph_start=0;
	int st_start;
	int st_clause_end;
	int count;
	int n_primary;
	int count_primary;
	PHONEME_TAB *ph;
	int ph_end=n_phoneme_list;

	SYLLABLE syllable_tab2[N_PHONEME_LIST];

	syllable_tab = syllable_tab2;   // don't use permanent storage. it's only needed during the call of CalcPitches()
	n_st = 0;
	n_primary = 0;
	for(ix=0; ix<(n_phoneme_list-1); ix++)
	{
		p = &phoneme_list[ix];
		if(p->synthflags & SFLAG_SYLLABLE)
		{
			syllable_tab[n_st].flags = 0;
			syllable_tab[n_st].env = PITCHfall;
			syllable_tab[n_st].nextph_type = phoneme_list[ix+1].type;
			syllable_tab[n_st++].stress = p->stresslevel;

			if(p->stresslevel >= 4)
				n_primary++;
		}
		else
		if((p->ph->code == phonPAUSE_CLAUSE) && (n_st > 0))
		{
			syllable_tab[n_st-1].flags |= SYL_END_CLAUSE;
		}
	}
	syllable_tab[n_st].stress = 0;   // extra 0 entry at the end

	if(n_st == 0)
		return;  // nothing to do



	if(tr->langopts.tone_language == 1)
	{
		CalcPitches_Tone(tr,clause_type);
		return;
	}


	option = tr->langopts.intonation_group;
	if(option >= INTONATION_TYPES)
		option = 1;

	if(option == 0)
	{
		group_tone = tr->langopts.tunes[clause_type];
		group_tone_emph = tr->langopts.tunes[5];
		group_tone_comma = tr->langopts.tunes[1];
	}
	else
	{
		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(clause_type == 4)
		no_tonic = 1;       /* incomplete clause, used for abbreviations such as Mr. Dr. Mrs. */
	else
		no_tonic = 0;

	st_start = 0;
	count_primary=0;
	for(st_ix=0; st_ix<n_st; st_ix++)
	{
		syl = &syllable_tab[st_ix];

		if(syl->stress >= 4)
			count_primary++;

		if(syl->stress == 6)
		{
			// reduce the stress of the previous stressed syllable (review only the previous few syllables)
			for(ix=st_ix-1; ix>=st_start && ix>=(st_ix-3); ix--)
			{
				if(syllable_tab[ix].stress == 6)
					break;
				if(syllable_tab[ix].stress == 4)
				{
					syllable_tab[ix].stress = 3;
					break;
				}
			}

			// are the next primary syllables also emphasized ?
			for(ix=st_ix+1; ix<n_st; ix++)
			{
				if(syllable_tab[ix].stress == 4)
					break;
				if(syllable_tab[ix].stress == 6)
				{
					// emphasize this syllable, but don't end the current tone group
					syllable_tab[st_ix].flags = SYL_EMPHASIS;
					syl->stress = 5;
					break;
				}
			}
		}

		if(syl->stress == 6)
		{
			// an emphasized syllable, end the tone group after the next primary stress
			syllable_tab[st_ix].flags = SYL_EMPHASIS;

			count = 0;
			if((n_primary - count_primary) > 1)
				count =1;

			for(ix=st_ix+1; ix<n_st; ix++)
			{
				if(syllable_tab[ix].stress > 4)
					break;
				if(syllable_tab[ix].stress == 4)
				{
					count++;
					if(count > 1)
						break;
				}
			}

			count_pitch_vowels(st_start, ix, n_st);
			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(option, st_start, ix, group_tone);

			st_start = ix;
		}
		if((st_start < st_ix) && (syl->flags & SYL_END_CLAUSE))
		{
			// 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(option, st_start, st_clause_end, group_tone_comma);
			st_start = st_clause_end;
		}
	}

	if(st_start < st_ix)
	{
		count_pitch_vowels(st_start, st_ix, n_st);
		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];

			p->pitch1 = syl->pitch1;
			p->pitch2 = syl->pitch2;

			p->env = PITCHfall;
			if(syl->flags & SYL_RISE)
			{
				p->env = PITCHrise;
			}
			else
			if(p->stresslevel > 5)
				p->env = syl->env;

			if(p->pitch1 > p->pitch2)
			{
				// swap so that pitch2 is the higher
				x = p->pitch1;
				p->pitch1 = p->pitch2;
				p->pitch2 = x;
			}

if(p->tone_ph)
{
	ph = phoneme_tab[p->tone_ph];
	x = (p->pitch1 + p->pitch2)/2;
	p->pitch2 = x + ph->end_type;
	p->pitch1 = x + ph->start_type;
}

			if(syl->flags & SYL_EMPHASIS)
			{
				p->stresslevel |= 8;      // emphasized
			}
	
			st_ix++;
		}
	}

}  // end of CalcPitches

 

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
CEO bring-it-together s.r.o.
Slovakia Slovakia
Jozef Božek is currently a software engineer at bring-it-together s.r.o. in area of large scale infomation systems and mobile applications development.
He has been developing in C++ nearly full time since 2000, in Java since 2004 and in Objective-C since 2009. He is programming using Java EE SDK, iOS SDK, COM/DCOM, MFC, ATL, STL and so on Smile | :)

Comments and Discussions