Click here to Skip to main content
15,891,136 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 74.6K   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 <ctype.h>
#include <wctype.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

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

int option_mbrola_phonemes;

#ifdef INCLUDE_MBROLA

extern int Read4Bytes(FILE *f);
extern void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range);
extern unsigned char *outbuf;

#ifndef PLATFORM_WINDOWS

#include "mbrowrap.h"

#else
#include <windows.h>
typedef void (WINAPI *PROCVV)(void);
typedef void (WINAPI *PROCVI)(int);
typedef void (WINAPI *PROCVF)(float);
typedef int (WINAPI *PROCIV)();
typedef int (WINAPI *PROCIC) (char *);
typedef int (WINAPI *PROCISI)(short *,int);
typedef char* (WINAPI *PROCVCI)(char *,int);

PROCIC		init_MBR;
PROCIC		write_MBR;
PROCIV		flush_MBR;
PROCISI		read_MBR;
PROCVV		close_MBR;
PROCVV		reset_MBR;
PROCIV		lastError_MBR;
PROCVCI		lastErrorStr_MBR;
PROCVI		setNoError_MBR;
PROCIV		getFreq_MBR;
PROCVF		setVolumeRatio_MBR;



HINSTANCE	hinstDllMBR = NULL;


BOOL load_MBR()
{
	if(hinstDllMBR != NULL)
		return TRUE;   // already loaded 

	if ((hinstDllMBR=LoadLibraryA("mbrola.dll")) == 0)
		return FALSE;
	init_MBR			=(PROCIC) GetProcAddress(hinstDllMBR,"init_MBR");
	write_MBR			=(PROCIC) GetProcAddress(hinstDllMBR,"write_MBR");
	flush_MBR			=(PROCIV) GetProcAddress(hinstDllMBR,"flush_MBR");
	read_MBR			=(PROCISI) GetProcAddress(hinstDllMBR,"read_MBR");
	close_MBR			=(PROCVV) GetProcAddress(hinstDllMBR,"close_MBR");
	reset_MBR			=(PROCVV) GetProcAddress(hinstDllMBR,"reset_MBR");
	lastError_MBR		=(PROCIV) GetProcAddress(hinstDllMBR,"lastError_MBR");
	lastErrorStr_MBR	=(PROCVCI) GetProcAddress(hinstDllMBR,"lastErrorStr_MBR");
	setNoError_MBR		=(PROCVI) GetProcAddress(hinstDllMBR,"setNoError_MBR");
	setVolumeRatio_MBR	=(PROCVF) GetProcAddress(hinstDllMBR,"setVolumeRatio_MBR");
	return TRUE;
}


void unload_MBR()
{
	if (hinstDllMBR)
	{
		FreeLibrary (hinstDllMBR);
		hinstDllMBR=NULL;
	}
}

#endif   // windows


static MBROLA_TAB *mbrola_tab = NULL;
static int mbrola_control = 0;


espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate)
{//===================================================================================
// Load a phoneme name translation table from espeak-data/mbrola

	int size;
	int ix;
	int *pw;
	FILE *f_in;
	char path[sizeof(path_home)+15];

	mbrola_name[0] = 0;
	mbrola_delay = 0;

	if(mbrola_voice == NULL)
	{
		samplerate = samplerate_native;
		SetParameter(espeakVOICETYPE,0,0);
		return(EE_OK);
	}

	sprintf(path,"%s/mbrola/%s",path_home,mbrola_voice);
#ifdef PLATFORM_POSIX
	if(GetFileLength(path) <= 0)
	{
		// mbrola voice file not found, look in /usr/share
     sprintf(path,"/usr/share/mbrola/%s",mbrola_voice);
	}
	if(GetFileLength(path) <= 0)
	{
		// mbrola voice file not found, look in /usr/share
     sprintf(path,"/usr/share/mbrola/voices/%s",mbrola_voice);
	}
#endif
#ifdef PLATFORM_WINDOWS
	if(load_MBR() == FALSE)     // load mbrola.dll
		return(EE_INTERNAL_ERROR); 
#endif

	if(init_MBR(path) != 0)      // initialise the required mbrola voice
		return(EE_NOT_FOUND);

	setNoError_MBR(1);     // don't stop on phoneme errors

	// read eSpeak's mbrola phoneme translation data, eg. en1_phtrans
	sprintf(path,"%s/mbrola_ph/%s",path_home,phtrans);
	size = GetFileLength(path);
	if((f_in = fopen(path,"rb")) == NULL) {
		close_MBR();	
		return(EE_NOT_FOUND);
	}

	if((mbrola_tab = (MBROLA_TAB *)realloc(mbrola_tab,size)) == NULL)
	{
		fclose(f_in);
		close_MBR();	
		return(EE_INTERNAL_ERROR);
	}

	mbrola_control = Read4Bytes(f_in);
	pw = (int *)mbrola_tab;
	for(ix=4; ix<size; ix+=4)
	{
		*pw++ = Read4Bytes(f_in);
	}
	size = fread(mbrola_tab,1,size,f_in);
	fclose(f_in);

	setVolumeRatio_MBR((float)(mbrola_control & 0xff) /16.0f);
//	srate = getFreq_MBR(); 
	samplerate = srate;
	if(srate == 22050)
		SetParameter(espeakVOICETYPE,0,0);
	else
		SetParameter(espeakVOICETYPE,1,0);
	strcpy(mbrola_name,mbrola_voice);
//	mbrola_delay = 3800;  // improve synchronization of events
	mbrola_delay = 1000;  // improve synchronization of events
	return(EE_OK);
}  // end of LoadMbrolaTable


static int GetMbrName(PHONEME_LIST *plist, PHONEME_TAB *ph, PHONEME_TAB *ph_prev, PHONEME_TAB *ph_next, int *name2, int *split, int *control)
{//==========================================================================================================================================
// Look up a phoneme in the mbrola phoneme name translation table
// It may give none, 1, or 2 mbrola phonemes
	int mnem = ph->mnemonic;
	MBROLA_TAB *pr;
	PHONEME_TAB *other_ph;
	int found = 0;

	// control
	// bit 0  skip the next phoneme
	// bit 1  match this and Previous phoneme
	// bit 2  only at the start of a word
	// bit 3  don't match two phonemes across a word boundary

	pr = mbrola_tab;
	while(pr->name != 0)
	{
		if(mnem == pr->name)
		{
			if(pr->next_phoneme == 0)
				found = 1;
			else
			if((pr->next_phoneme == ':') && (plist->synthflags & SFLAG_LENGTHEN))
			{
				found = 1;
			}
			else
			{
				if(pr->control & 2)
					other_ph = ph_prev;
				else
				if((pr->control & 8) && ((plist+1)->newword))
					other_ph = phoneme_tab[phPAUSE];  // don't match the next phoneme over a word boundary
				else
					other_ph = ph_next;

				if((pr->next_phoneme == other_ph->mnemonic) ||
					((pr->next_phoneme == 2) && (other_ph->type == phVOWEL)) ||
					((pr->next_phoneme == '_') && (other_ph->type == phPAUSE)))
				{
					found = 1;
				}
			}

			if((pr->control & 4) && (plist->newword == 0))  // only at start of word
				found = 0;

			if(found)
			{
				*name2 = pr->mbr_name2;
				*split = pr->percent;
				*control = pr->control;
				return(pr->mbr_name);
			}
		}

		pr++;
	}
	*name2=0;
	*split=0;
	*control=0;
	return(mnem);
}


static char *WritePitch(int env, int pitch1, int pitch2, int split, int final)
{//===========================================================================
// final=1:  only give the final pitch value.
	int x;
	int ix;
	int pitch_base;
	int pitch_range;
	int p1,p2,p_end;
	unsigned char *pitch_env;
	int max = -1;
	int min = 999;
	int y_max=0;
	int y_min=0;
	int env100 = 80;  // apply the pitch change only over this proportion of the mbrola phoneme(s)
	int y2;
	int y[4];
	int env_split;
	char buf[50];
	static char output[50];

	output[0] = 0;
	pitch_env = envelope_data[env];


	SetPitch2(voice, pitch1, pitch2, &pitch_base, &pitch_range);


	env_split = (split * 128)/100;
	if(env_split < 0)
		env_split = 0-env_split;

	// find max and min in the pitch envelope
	for(x=0; x<128; x++)
	{
		if(pitch_env[x] > max)
		{
			max = pitch_env[x];
			y_max = x;
		}
		if(pitch_env[x] < min)
		{
			min = pitch_env[x];
			y_min = x;
		}
	}
	// set an additional pitch point half way through the phoneme.
	// but look for a maximum or a minimum and use that instead
	y[2] = 64;
	if((y_max > 0) && (y_max < 127))
	{
		y[2] = y_max;
	}
	if((y_min > 0) && (y_min < 127))
	{
		y[2] = y_min;
	}
	y[1] = y[2] / 2;
	y[3] = y[2] + (127 - y[2])/2;

	// set initial pitch
	p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base;   // Hz << 12
	p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base;


	if(split >= 0)
	{
		sprintf(buf," 0 %d",p1/4096);
		strcat(output,buf);
	}

	// don't use intermediate pitch points for linear rise and fall
	if(env > 1)
	{
		for(ix=1; ix<4; ix++)
		{
			p2 = ((pitch_env[y[ix]]*pitch_range)>>8) + pitch_base;

			if(split > 0)
			{
				y2 = (y[ix] * env100)/env_split;
			}
			else
			if(split < 0)
			{
				y2 = ((y[ix]-env_split) * env100)/env_split;
			}
			else
			{
				y2 = (y[ix] * env100)/128;
			}
			if((y2 > 0) && (y2 <= env100))
			{
				sprintf(buf," %d %d",y2,p2/4096);
				strcat(output,buf);
			}
		}
	}

	p_end = p_end/4096;
	if(split <= 0)
	{
		sprintf(buf," %d %d",env100,p_end);
		strcat(output,buf);
	}
	if(env100 < 100)
	{
		sprintf(buf," %d %d",100,p_end);
		strcat(output,buf);
	}
	strcat(output,"\n");

	if(final)
		sprintf(output,"\t100 %d\n",p_end);
	return(output);
}  // end of WritePitch


int MbrolaTranslate(PHONEME_LIST *plist, int n_phonemes, int resume, FILE *f_mbrola)
{//=================================================================================
// Generate a mbrola pho file
	unsigned int name;
	int len;
	int len1;
	PHONEME_TAB *ph;
	PHONEME_TAB *ph_next;
	PHONEME_TAB *ph_prev;
	PHONEME_LIST *p;
	PHONEME_LIST *next;
	PHONEME_LIST *prev;
	PHONEME_DATA phdata;
	FMT_PARAMS fmtp;
	int pause = 0;
	int released;
	int name2;
	int control;
	int done;
	int len_percent;
	const char *final_pitch;
	char *ptr;
	char mbr_buf[120];

	static int phix;
	static int embedded_ix;
	static int word_count;

	if (!resume) {
		phix = 1;
		embedded_ix = 0;
		word_count = 0;
	}

	while (phix < n_phonemes)
	{
		if (WcmdqFree() < MIN_WCMDQ)
			return 1;

		ptr = mbr_buf;

		p = &plist[phix];
		next = &plist[phix+1];
		prev = &plist[phix-1];
		ph = p->ph;
		ph_prev = plist[phix-1].ph;
		ph_next = plist[phix+1].ph;

		if(p->synthflags & SFLAG_EMBEDDED)
		{
			DoEmbedded(&embedded_ix, p->sourceix);
		}

		if(p->newword & 4)
			DoMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences);
		if(p->newword & 1)
			DoMarker(espeakEVENT_WORD, (p->sourceix & 0x7ff) + clause_start_char, p->sourceix >> 11, clause_start_word + word_count++);

		name = GetMbrName(p,ph,ph_prev,ph_next,&name2,&len_percent,&control);
		if(control & 1)
			phix++;

		if(name == 0) {
			phix++;
			continue;   // ignore this phoneme
		}

		if((ph->type == phPAUSE) && (name == ph->mnemonic))
		{
			// a pause phoneme, which has not been changed by the translation
			name = '_';
			len = (p->length * speed.pause_factor)/256;
//			if(len == 0) continue;
			if(len == 0)
				len = 1;
		}
		else
			len = (80 * speed.wav_factor)/256;

		DoMarker(espeakEVENT_PHONEME, (p->sourceix & 0x7ff) + clause_start_char, 0, ph->mnemonic); 

		ptr += sprintf(ptr,"%s\t",WordToString(name));

		if(name2 == '_')
		{
			// add a pause after this phoneme
			pause = len_percent;
			name2 = 0;
		}

		done = 0;
		final_pitch = "";

		switch(ph->type)
		{
		case phVOWEL:
			len = ph->std_length;
			if(p->synthflags & SFLAG_LENGTHEN)
				len += phoneme_tab[phonLENGTHEN]->std_length;  // phoneme was followed by an extra : symbol

			if(ph_next->type == phPAUSE)
				len += 50;        // lengthen vowels before a pause
			len = (len * p->length)/256;

			if(name2 == 0)
			{
				char *pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,0);
				ptr += sprintf(ptr,"%d\t%s", len, pitch);
			}
			else
			{
				char *pitch;

				pitch = WritePitch(p->env,p->pitch1,p->pitch2,len_percent,0);
				len1 = (len * len_percent)/100;
				ptr += sprintf(ptr,"%d\t%s", len1, pitch);

				pitch = WritePitch(p->env,p->pitch1,p->pitch2,-len_percent,0);
				ptr += sprintf(ptr,"%s\t%d\t%s", WordToString(name2), len-len1, pitch);
			}
			done = 1;
			break;

		case phSTOP:
			released = 0;
			if(next->type==phVOWEL) released = 1;
			if(next->type==phLIQUID && !next->newword) released = 1;

			if(released == 0)
				p->synthflags |= SFLAG_NEXT_PAUSE;
			InterpretPhoneme(NULL, 0, p, &phdata);
			len = DoSample3(&phdata, 0, -1);

			len = (len * 1000)/samplerate;  // convert to mS
			len += PauseLength(p->prepause,1);
			break;

		case phVSTOP:
			len = (80 * speed.wav_factor)/256;
			break;

		case phFRICATIVE:
			len = 0;
			InterpretPhoneme(NULL, 0, p, &phdata);
			if(p->synthflags & SFLAG_LENGTHEN)
				len = DoSample3(&phdata, p->length, -1);  // play it twice for [s:] etc.
			len += DoSample3(&phdata, p->length, -1);

			len = (len * 1000)/samplerate;  // convert to mS
			break;

		case phNASAL:
			if(next->type != phVOWEL)
			{
				memset(&fmtp, 0, sizeof(fmtp));
				InterpretPhoneme(NULL, 0, p, &phdata);
				fmtp.fmt_addr = phdata.sound_addr[pd_FMT];
				len = DoSpect2(p->ph, 0, &fmtp,  p, -1);
//				len = DoSpect(p->ph,prev->ph,phoneme_tab[phonPAUSE],2,p,-1);
				len = (len * 1000)/samplerate;
				if(next->type == phPAUSE)
					len += 50;
				final_pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,1);
			}
			break;

		case phLIQUID:
			if(next->type == phPAUSE)
			{
				len += 50;
				final_pitch = WritePitch(p->env,p->pitch1,p->pitch2,0,1);
			}
			break;
		}

		if(!done)
		{
			if(name2 != 0)
			{
				len1 = (len * len_percent)/100;
				ptr += sprintf(ptr,"%d\n%s\t",len1,WordToString(name2));
				len -= len1;
			}
			ptr += sprintf(ptr,"%d%s\n",len,final_pitch);
		}

		if(pause)
		{
			len += PauseLength(pause,0); 
			ptr += sprintf(ptr,"_ \t%d\n",PauseLength(pause,0));
			pause = 0;
		}

		if(f_mbrola)
		{
			fwrite(mbr_buf,1,(ptr-mbr_buf),f_mbrola);  // write .pho to a file
		}
		else
		{
			int res = write_MBR(mbr_buf);
			if (res < 0)
				return 0;  /* don't get stuck on error */
			if (res == 0)
				return 1;
			wcmdq[wcmdq_tail][0] = WCMD_MBROLA_DATA;
			wcmdq[wcmdq_tail][1] = len;
			WcmdqInc();
		}

		phix++;
	}

	if(!f_mbrola)
	{
		flush_MBR();

		// flush the mbrola output buffer
		wcmdq[wcmdq_tail][0] = WCMD_MBROLA_DATA;
		wcmdq[wcmdq_tail][1] = 500;
		WcmdqInc();
	}

	return 0;
}  // end of MbrolaTranslate


int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
{//==================================================================
	FILE *f_mbrola = NULL;

	if(*n_ph == 0)
		return(0);

	if(option_mbrola_phonemes)
	{
		// send mbrola data to a file, not to the mbrola library
		f_mbrola = f_trans;
	}

	int again = MbrolaTranslate(phoneme_list, *n_ph, resume, f_mbrola);
	if (!again)
		*n_ph = 0;
	return again; 
}


int MbrolaFill(int length, int resume)
{//===================================
// Read audio data from Mbrola (length is in milisecs)

	static int n_samples;
	int req_samples, result;

	if (!resume)
		n_samples = samplerate * length / 1000;

	req_samples = (out_end - out_ptr)/2;
	if (req_samples > n_samples)
		req_samples = n_samples;
	result = read_MBR((short *)out_ptr, req_samples);
	if (result <= 0)
		return 0;
	out_ptr += result*2;
	n_samples -= result;
	return n_samples ? 1 : 0;
}


void MbrolaReset(void)
{//===================
// Reset the Mbrola engine and flush the pending audio

	reset_MBR();
}

#else   // INCLUDE_MBROLA

// mbrola interface is not compiled, provide dummy functions.

espeak_ERROR LoadMbrolaTable(const char *mbrola_voice, const char *phtrans, int srate)
{
	return(EE_INTERNAL_ERROR); 
}

int MbrolaGenerate(PHONEME_LIST *phoneme_list, int *n_ph, int resume)
{
	return(0);
}

int MbrolaFill(int length, int resume)
{
	return(0);
}

void MbrolaReset(void)
{
}


#endif  // INCLUDE_MBROLA

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