1 /*
2  * Copyright (C) 2005 to 2014 by Jonathan Duddington
3  * email: jonsd@users.sourceforge.net
4  * Copyright (C) 2013-2016 Reece H. Dunn
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see: <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include <errno.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "espeak_ng.h"
29 
30 #include "error.h"
31 #include "speech.h"
32 #include "synthesize.h"
33 
basename(const char * filename)34 static const char *basename(const char *filename)
35 {
36 	const char *current = filename + strlen(filename);
37 	while (current != filename && !(*current == '/' || *current == '\\'))
38 		--current;
39 	return current == filename ? current : current + 1;
40 }
41 
StringToWord(const char * string)42 static unsigned int StringToWord(const char *string)
43 {
44 	// Pack 4 characters into a word
45 	int ix;
46 	unsigned char c;
47 	unsigned int word;
48 
49 	if (string == NULL)
50 		return 0;
51 
52 	word = 0;
53 	for (ix = 0; ix < 4; ix++) {
54 		if (string[ix] == 0) break;
55 		c = string[ix];
56 		word |= (c << (ix*8));
57 	}
58 	return word;
59 }
60 
61 #pragma GCC visibility push(default)
espeak_ng_CompileMbrolaVoice(const char * filepath,FILE * log,espeak_ng_ERROR_CONTEXT * context)62 espeak_ng_STATUS espeak_ng_CompileMbrolaVoice(const char *filepath, FILE *log, espeak_ng_ERROR_CONTEXT *context)
63 {
64 	if (!log) log = stderr;
65 
66 	char *p;
67 	FILE *f_in;
68 	FILE *f_out;
69 	int percent;
70 	int n;
71 	int *pw;
72 	int *pw_end;
73 	int count = 0;
74 	int control;
75 	char phoneme[40];
76 	char phoneme2[40];
77 	char name1[40];
78 	char name2[40];
79 	char mbrola_voice[40];
80 	char buf[sizeof(path_home)+30];
81 	int mbrola_ctrl = 20; // volume in 1/16 ths
82 	MBROLA_TAB data[N_PHONEME_TAB];
83 
84 	strcpy(buf, filepath);
85 	if ((f_in = fopen(buf, "r")) == NULL)
86 		return create_file_error_context(context, (espeak_ng_STATUS) errno, buf);
87 
88 	while (fgets(buf, sizeof(phoneme), f_in) != NULL) {
89 		buf[sizeof(phoneme)-1] = 0;
90 
91 		if ((p = strstr(buf, "//")) != NULL)
92 			*p = 0; // truncate line at comment
93 
94 		if (memcmp(buf, "volume", 6) == 0) {
95 			mbrola_ctrl = atoi(&buf[6]);
96 			continue;
97 		}
98 
99 		n = sscanf(buf, "%d %s %s %d %s %s", &control, phoneme, phoneme2, &percent, name1, name2);
100 		if (n >= 5) {
101 			data[count].name = StringToWord(phoneme);
102 			if (strcmp(phoneme2, "NULL") == 0)
103 				data[count].next_phoneme = 0;
104 			else if (strcmp(phoneme2, "VWL") == 0)
105 				data[count].next_phoneme = 2;
106 			else
107 				data[count].next_phoneme = StringToWord(phoneme2);
108 			data[count].mbr_name = 0;
109 			data[count].mbr_name2 = 0;
110 			data[count].percent = percent;
111 			data[count].control = control;
112 			if (strcmp(name1, "NULL") != 0)
113 				data[count].mbr_name = StringToWord(name1);
114 			if (n == 6)
115 				data[count].mbr_name2 = StringToWord(name2);
116 
117 			count++;
118 		}
119 	}
120 	fclose(f_in);
121 
122 	strcpy(mbrola_voice, basename(filepath));
123 	sprintf(buf, "%s/mbrola_ph/%s_phtrans", path_home, mbrola_voice);
124 	if ((f_out = fopen(buf, "wb")) == NULL)
125 		return create_file_error_context(context, (espeak_ng_STATUS) errno, buf);
126 
127 	memset(&data[count], 0, sizeof(data[count]));
128 	data[count].name = 0; // list terminator
129 	Write4Bytes(f_out, mbrola_ctrl);
130 
131 	pw_end = (int *)(&data[count+1]);
132 	for (pw = (int *)data; pw < pw_end; pw++)
133 		Write4Bytes(f_out, *pw);
134 	fclose(f_out);
135 	fprintf(log, "Mbrola translation file: %s -- %d phonemes\n", buf, count);
136 	return ENS_OK;
137 }
138 #pragma GCC visibility pop
139