Click here to Skip to main content
15,885,985 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 74K   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 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/>.                           *
 ***************************************************************************/


#include "StdAfx.h"

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


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

const char *version_string = "1.44.04  14.Sep.10";
const int version_phdata  = 0x014404;

int option_device_number = -1;
FILE *f_logespeak = NULL;
int logging_type;

// copy the current phoneme table into here
int n_phoneme_tab;
int current_phoneme_table;
PHONEME_TAB *phoneme_tab[N_PHONEME_TAB];
unsigned char phoneme_tab_flags[N_PHONEME_TAB];   // bit 0: not inherited

USHORT *phoneme_index=NULL;
char *phondata_ptr=NULL;
unsigned char *wavefile_data=NULL;
static unsigned char *phoneme_tab_data = NULL;

int n_phoneme_tables;
PHONEME_TAB_LIST phoneme_tab_list[N_PHONEME_TABS];
int phoneme_tab_number = 0;

int wavefile_ix;              // a wavefile to play along with the synthesis
int wavefile_amp;
int wavefile_ix2;
int wavefile_amp2;

int seq_len_adjust;
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);



static char *ReadPhFile(void *ptr, const char *fname, int *size)
{//=============================================================
	FILE *f_in;
	char *p;
	unsigned int  length;
	char buf[sizeof(path_home)+40];

	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);
		return(NULL);
	}

	if(ptr != NULL)
		Free(ptr);
		
	if((p = Alloc(length)) == NULL)
	{
		fclose(f_in);
		return(NULL);
	}
	if(fread(p,1,length,f_in) != length)
	{
		fclose(f_in);
		return(NULL);
	}

	fclose(f_in);
	if(size != NULL)
		*size = length;
	return(p);
}  //  end of ReadPhFile


int LoadPhData()
{//=============
	int ix;
	int n_phonemes;
	int version;
	int result = 1;
	int length;
	unsigned char *p;
	int *pw;

	if((phoneme_tab_data = (unsigned char *)ReadPhFile((void *)(phoneme_tab_data),"phontab",NULL)) == NULL)
		return(-1);
	if((phoneme_index = (USHORT *)ReadPhFile((void *)(phoneme_index),"phonindex",NULL)) == NULL)
		return(-1);
	if((phondata_ptr = ReadPhFile((void *)(phondata_ptr),"phondata",NULL)) == NULL)
		return(-1);
	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;
	for(ix=0; ix<4; ix++)
	{
		version += (wavefile_data[ix] << (ix*8));
	}

	if(version != version_phdata)
	{
		result = version;
	}

	// set up phoneme tables
	p = phoneme_tab_data;
	n_phoneme_tables = p[0];
	p+=4;

	for(ix=0; ix<n_phoneme_tables; ix++)
	{
		n_phonemes = p[0];
		phoneme_tab_list[ix].n_phonemes = p[0];
		phoneme_tab_list[ix].includes = p[1];
		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;
		p += (n_phonemes * sizeof(PHONEME_TAB));
	}

	if(phoneme_tab_number >= n_phoneme_tables)
		phoneme_tab_number = 0;

	return(result);
}  //  end of LoadPhData


void FreePhData(void)
{//==================
	Free(phoneme_tab_data);
	Free(phoneme_index);
	Free(phondata_ptr);
	phoneme_tab_data=NULL;
	phoneme_index=NULL;
	phondata_ptr=NULL;
}


int PhonemeCode(unsigned int mnem)
{//===============================
	int ix;

	for(ix=0; ix<n_phoneme_tab; ix++)
	{
		if(phoneme_tab[ix] == NULL)
			continue;
		if(phoneme_tab[ix]->mnemonic == mnem)
			return(phoneme_tab[ix]->code);
	}
	return(0);
}


int LookupPhonemeString(const char *string)
{//========================================
	int  ix;
	unsigned char c;
	unsigned int  mnem;

	// Pack up to 4 characters into a word
	mnem = 0;
	for(ix=0; ix<4; ix++)
	{
		if(string[ix]==0) break;
		c = string[ix];
		mnem |= (c << (ix*8));
	}

	return(PhonemeCode(mnem));
}




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;
	int  seq_break;
	frameref_t *frames;
	int  length1;
	int  length_std;
	int  length_factor;
	SPECT_SEQ *seq, *seq2;
	SPECT_SEQK *seqk, *seqk2;
	frame_t *frame;
	static frameref_t frames_buf[N_SEQ_FRAMES];
	
	seq = (SPECT_SEQ *)(&phondata_ptr[fmt_params->fmt_addr]);
	seqk = (SPECT_SEQK *)seq;
	nf = seq->n_frames;


	if(nf >= N_SEQ_FRAMES)
		nf = N_SEQ_FRAMES - 1;

	seq_len_adjust = fmt_params->fmt2_lenadj + fmt_params->fmt_length;
	seq_break = 0;

	for(ix=0; ix<nf; ix++)
	{
		if(seq->frame[0].frflags & FRFLAG_KLATT)
			frame = &seqk->frame[ix];
		else
			frame = (frame_t *)&seq->frame[ix];
		frames_buf[ix].frame = frame;
		frames_buf[ix].frflags = frame->frflags;
		frames_buf[ix].length = frame->length;
		if(frame->frflags & FRFLAG_VOWEL_CENTRE)
			seq_break = ix;
	}

	frames = &frames_buf[0];
	if(seq_break > 0)
	{
		if(which==1)
		{
			nf = seq_break + 1;
		}
		else
		{
			frames = &frames_buf[seq_break];  // body of vowel, skip past initial frames
			nf -= seq_break;
		}
	}
	
	// do we need to modify a frame for blending with a consonant?
	if((this_ph->type == phVOWEL) && (fmt_params->fmt2_addr == 0) && (fmt_params->use_vowelin))
	{
		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(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 *)(&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++)
		{
			if(seq2->frame[0].frflags & FRFLAG_KLATT)
				frame = &seqk2->frame[ix];
			else
				frame = (frame_t *)&seq2->frame[ix];

			frames[nf].length = frame->length;
			if(ix > 0)
			{
				frames[nf].frame = frame;
				frames[nf].frflags = frame->frflags;
			}
			nf++;
		}
		wavefile_ix = 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 = 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 * 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;
			}
		}
		else
		{
			if(which == 1)
			{
				// 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;
				}
			}
			else
			{
				//not a vowel
				if(fmt_params->std_length > 0)
				{
					seq_len_adjust += (fmt_params->std_length - length1);
				}
			}

			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;
				}
			}
		}
	}
	
	*n_frames = nf;
	return(frames);
}  //  end of LookupSpect



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


static void SetUpPhonemeTable(int number, int recursing)
{//=====================================================
	int ix;
	int includes;
	int ph_code;
	PHONEME_TAB *phtab;

	if(recursing==0)
	{
		memset(phoneme_tab_flags,0,sizeof(phoneme_tab_flags));
	}

	if((includes = phoneme_tab_list[number].includes) > 0)
	{
		// recursively include base phoneme tables
		SetUpPhonemeTable(includes-1,1);
	}

	// now add the phonemes from this table
	phtab = phoneme_tab_list[number].phoneme_tab_ptr;
	for(ix=0; ix<phoneme_tab_list[number].n_phonemes; ix++)
	{
		ph_code = phtab[ix].code;
		phoneme_tab[ph_code] = &phtab[ix];
		if(ph_code > n_phoneme_tab)
			n_phoneme_tab = ph_code;

		if(recursing == 0)
			phoneme_tab_flags[ph_code] |= 1;   // not inherited
	}
}  // end of SetUpPhonemeTable


void SelectPhonemeTable(int number)
{//================================
	n_phoneme_tab = 0;
	SetUpPhonemeTable(number,0);  // recursively for included phoneme tables
	n_phoneme_tab++;
	current_phoneme_table = number;
}  //  end of SelectPhonemeTable


int LookupPhonemeTable(const char *name)
{//=====================================
	int ix;

	for(ix=0; ix<n_phoneme_tables; ix++)
	{
		if(strcmp(name,phoneme_tab_list[ix].name)==0)
		{
			phoneme_tab_number = ix;
			break;
		}
	}
	if(ix == n_phoneme_tables)
		return(-1);

	return(ix);
}


int SelectPhonemeTableName(const char *name)
{//=========================================
// Look up a phoneme set by name, and select it if it exists
// Returns the phoneme table number
	int ix;

	if((ix = LookupPhonemeTable(name)) == -1)
		return(-1);

	SelectPhonemeTable(ix);
	return(ix);
}  //  end of DelectPhonemeTableName




void LoadConfig(void)
{//==================
// Load configuration file, if one exists
	char buf[sizeof(path_home)+10];
	FILE *f;
	int ix;
	char c1;
	char *p;
	char string[200];

	logging_type = 0;

	for(ix=0; ix<N_SOUNDICON_SLOTS; ix++)
	{
		soundicon_tab[ix].filename = NULL;
		soundicon_tab[ix].data = NULL;
	}

	sprintf(buf,"%s%c%s",path_home,PATHSEP,"config");
	if((f = fopen(buf,"r"))==NULL)
	{
		return;
	}

	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);
		}
		else
		if(memcmp(buf,"pa_device",9)==0)
		{
			sscanf(&buf[7],"%d",&option_device_number);
		}
		else
		if(memcmp(buf,"soundicon",9)==0)
		{
			ix = sscanf(&buf[10],"_%c %s",&c1,string);
			if(ix==2)
			{
				soundicon_tab[n_soundicon_tab].name = c1;
				p = Alloc(strlen(string)+1);
				strcpy(p,string);
				soundicon_tab[n_soundicon_tab].filename = p;
				soundicon_tab[n_soundicon_tab++].length = 0;
			}
		}
	}
}  //  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 bool InterpretCondition(Translator *tr, int control, PHONEME_LIST *plist, int instn)
{//========================================================================================
	int which;
	unsigned int data;
	int instn2;
	PHONEME_TAB *ph;
	PHONEME_LIST *plist_this;
	static int ph_position[8] = {0, 1, 2, 3, 2, 0, 1, 3};  // prevPh, thisPh, nextPh, next2Ph, nextPhW, prevPhW, nextVowel, next2PhW

	data = instn & 0xff;
	instn2 = instn >> 8;

	if(instn2 < 14)
	{
		which = (instn2) % 7;

		if(which==4)
		{
			// nextPh not word boundary
			if(plist[1].sourceix)
				return(false);
		}
		if(which==5)
		{
			// prevPh, not word boundary
			if(plist[0].sourceix)
				return(false);
		}
		if(which==6)
		{
			// nextVowel, not word boundary
			for(which=2;;which++)
			{
				if(plist[which-1].sourceix)
					return(false);
				if(phoneme_tab[plist[which-1].phcode]->type == phVOWEL)
					break;
			}
		}
		if(which==7)
		{
			// nextPh2 not word boundary
			if((plist[1].sourceix) || (plist[2].sourceix))
				return(false);
		}
		else
		{
			which = ph_position[which];
		}
		plist_this = plist;
		plist = &plist[which-1];

		if(which == 0)
		{
			if(plist->phcode == 1)
			{
				// This is a NULL phoneme, a phoneme has been deleted so look at the previous phoneme
				plist = &plist[-1];
			}
		}

		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));
			}
			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 SwitchVowelType(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


static int NumInstnWords(USHORT *prog)
{//===================================
	int instn;
	int instn2;
	int instn_type;
	static char n_words[11] = {1,1,1,1,1,1,1,1,1,2,4};

	instn = *prog;
	instn_type = instn >> 12;
	if(instn_type < 11)
		return(n_words[instn_type]);

	// 11 to 15, 2 words
	instn2 = prog[2];
	if((instn2 >> 12) == 0xf)
	{
		// 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)
{//==========================================================================================
// control:
//bit 0:  PreVoicing
//bit 8:  change phonemes
	PHONEME_TAB *ph;
	USHORT *prog;
	USHORT instn;
	int instn2;
	int or_flag;
	bool truth;
	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;

	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:
			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_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))
				{
					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
				if(or_flag)
					truth = (truth || InterpretCondition(tr, control, plist, instn & 0xfff));
				else
					truth = (truth && InterpretCondition(tr, control, plist, instn & 0xfff));
				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
				SwitchVowelType(plist, phdata, &prog, 2);
				break;

			case 6:   // PrevVowelTypeEndings
				SwitchVowelType(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];
		}
	}

}  // 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);
}  // end of InterpretPhoneme2

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