427 lines
7.8 KiB
C
427 lines
7.8 KiB
C
#include <fishtalk.h>
|
|
|
|
/* phonemes per letter */
|
|
#define MAX_PPL 5
|
|
|
|
#define FT_PHONEME(X) phon_##X,
|
|
static signed char* phons[] = {NULL, NULL, NULL, FT_PHONEMES};
|
|
#undef FT_PHONEME
|
|
|
|
#define FT_PHONEME(X) , #X
|
|
static const char* phon_str[] = {"(none)", "(space)", "" FT_PHONEMES};
|
|
#undef FT_PHONEME
|
|
|
|
static int p_match(const char* buffer, const char* target, long buffer_length) {
|
|
int i;
|
|
|
|
if(buffer_length < strlen(target)) return 0;
|
|
|
|
for(i = 0; target[i] != 0; i++) {
|
|
if(target[i] == '_') continue;
|
|
|
|
if(tolower(buffer[i]) != tolower(target[i])) return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define M(X) p_match(buffer + i, (X), length - i)
|
|
#define DEF(X) static int X(unsigned char* ph, int i, const char* buffer, long length, int first, int last, char b_c)
|
|
|
|
DEF(p_gen_4) {
|
|
if(M("tion")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_SH;
|
|
ph[i * MAX_PPL + 1] = FT_P_AH;
|
|
ph[i * MAX_PPL + 2] = FT_P_N;
|
|
return 3;
|
|
} else if(M("sion")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_ZH;
|
|
ph[i * MAX_PPL + 1] = FT_P_AH;
|
|
ph[i * MAX_PPL + 2] = FT_P_N;
|
|
return 3;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEF(p_silent_e) {
|
|
if(M("a_e")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_EY;
|
|
|
|
return 0;
|
|
} else if(M("i_e")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_AY;
|
|
|
|
return 0;
|
|
} else if(M("o_e")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_OW;
|
|
|
|
return 0;
|
|
} else if(M("u_e")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_Y;
|
|
ph[i * MAX_PPL + 1] = FT_P_UW;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEF(p_special_2) {
|
|
if(M("ly") && last) {
|
|
ph[i * MAX_PPL + 0] = FT_P_L;
|
|
ph[i * MAX_PPL + 1] = FT_P_IY;
|
|
|
|
return 1;
|
|
} else if(M("le") && last) {
|
|
ph[i * MAX_PPL + 0] = FT_P_L;
|
|
ph[i * MAX_PPL + 1] = FT_P_EH;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEF(p_gen_2) {
|
|
if(M("ll")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_L;
|
|
|
|
return 1;
|
|
} else if(M("sh")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_SH;
|
|
|
|
return 1;
|
|
} else if(M("ch")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_CH;
|
|
|
|
return 1;
|
|
} else if(M("th")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_TH;
|
|
i++;
|
|
|
|
return 1;
|
|
} else if(M("ph")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_F;
|
|
|
|
return 1;
|
|
} else if(M("ng")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_NG;
|
|
|
|
return 1;
|
|
} else if(M("wr")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_R;
|
|
|
|
return 1;
|
|
} else if(M("kn") || M("gn")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_N;
|
|
|
|
return 1;
|
|
} else if(M("ck")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_K;
|
|
|
|
return 1;
|
|
} else if(M("gi")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_G;
|
|
ph[i * MAX_PPL + 1] = FT_P_IY;
|
|
|
|
return 1;
|
|
} else if(M("wa")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_W;
|
|
ph[i * MAX_PPL + 1] = FT_P_AE;
|
|
|
|
return 1;
|
|
} else if(M("te")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_T;
|
|
ph[i * MAX_PPL + 1] = FT_P_EH;
|
|
|
|
return 1;
|
|
} else if(M("tt")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_T;
|
|
|
|
return 1;
|
|
} else if(M("do")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_D;
|
|
ph[i * MAX_PPL + 1] = FT_P_OW;
|
|
|
|
return 1;
|
|
} else if(M("ss")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_S;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEF(p_vowel_2) {
|
|
if(M("ee") || M("ea")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_EY;
|
|
|
|
return 1;
|
|
} else if(M("oo")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_UW; /* FIXME: could be FT_P_UH */
|
|
|
|
return 1;
|
|
} else if(M("ou")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_AW;
|
|
|
|
return 1;
|
|
} else if(M("ow")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_OW;
|
|
|
|
return 1;
|
|
} else if(M("oi") || M("oy")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_OY;
|
|
|
|
return 1;
|
|
} else if(M("rr")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_R;
|
|
|
|
return 1;
|
|
} else if(M("ar")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_AA;
|
|
ph[i * MAX_PPL + 1] = FT_P_R;
|
|
|
|
return 1;
|
|
} else if(M("er") || M("ir") || M("ur")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_ER;
|
|
|
|
return 1;
|
|
} else if(M("or")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_AO;
|
|
ph[i * MAX_PPL + 1] = FT_P_R;
|
|
|
|
return 1;
|
|
} else if(M("zz")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_Z;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEF(p_soft) {
|
|
if(M("c")) {
|
|
if(b_c == 'e' || b_c == 'i' || b_c == 'y') {
|
|
ph[i * MAX_PPL + 0] = FT_P_S;
|
|
} else {
|
|
ph[i * MAX_PPL + 0] = FT_P_K;
|
|
}
|
|
|
|
return 0;
|
|
} else if(M("g")) {
|
|
if(b_c == 'e' || b_c == 'i' || b_c == 'y') {
|
|
ph[i * MAX_PPL + 0] = FT_P_JH;
|
|
} else {
|
|
ph[i * MAX_PPL + 0] = FT_P_G;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEF(p_special_1) {
|
|
if(M("e") && last && tolower(b_c) != 'm') {
|
|
return 0;
|
|
} else if(M("y") && first) {
|
|
ph[i * MAX_PPL + 0] = FT_P_Y;
|
|
|
|
return 0;
|
|
} else if(M("y") && last) {
|
|
ph[i * MAX_PPL + 0] = FT_P_IY;
|
|
|
|
return 0;
|
|
} else if(M("n") && last) {
|
|
ph[i * MAX_PPL + 0] = FT_P_N;
|
|
|
|
return 0;
|
|
} else if(M("k") && last) {
|
|
ph[i * MAX_PPL + 0] = FT_P_K;
|
|
|
|
return 0;
|
|
} else if(M("x") && first) {
|
|
ph[i * MAX_PPL + 0] = FT_P_Z;
|
|
|
|
return 0;
|
|
} else if(M("y") && last) {
|
|
ph[i * MAX_PPL + 0] = FT_P_Y;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEF(p_gen_1) {
|
|
if(M("s")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_S;
|
|
|
|
return 0;
|
|
} else if(M("f")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_F;
|
|
|
|
return 0;
|
|
} else if(M("t")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_T;
|
|
|
|
return 0;
|
|
} else if(M("p")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_P;
|
|
|
|
return 0;
|
|
} else if(M("l")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_L;
|
|
|
|
return 0;
|
|
} else if(M("m")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_M;
|
|
|
|
return 0;
|
|
} else if(M("n")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_N;
|
|
|
|
return 0;
|
|
} else if(M("d")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_D;
|
|
|
|
return 0;
|
|
} else if(M("h")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_HH;
|
|
|
|
return 0;
|
|
} else if(M("r")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_R;
|
|
|
|
return 0;
|
|
} else if(M("v")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_V;
|
|
|
|
return 0;
|
|
} else if(M("w")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_W;
|
|
|
|
return 0;
|
|
} else if(M("x")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_K;
|
|
ph[i * MAX_PPL + 1] = FT_P_S;
|
|
|
|
return 0;
|
|
} else if(M("y")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_Y;
|
|
|
|
return 0;
|
|
} else if(M("z")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_Z;
|
|
|
|
return 0;
|
|
} else if(M("b")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_B;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEF(p_vowel_1) {
|
|
if(M("a")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_AE;
|
|
|
|
return 0;
|
|
} else if(M("e")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_EH;
|
|
|
|
return 0;
|
|
} else if(M("i")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_IH;
|
|
|
|
return 0;
|
|
} else if(M("o")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_AA;
|
|
|
|
return 0;
|
|
} else if(M("u")) {
|
|
ph[i * MAX_PPL + 0] = FT_P_AH;
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
#undef DEF
|
|
#undef M
|
|
|
|
void ft_phon_main(FT_HANDLE handle, const char* buffer, long length) {
|
|
unsigned char* ph = malloc(length * MAX_PPL);
|
|
int i;
|
|
|
|
memset(ph, FT_P_NONE, length * MAX_PPL);
|
|
|
|
for(i = 0; i < length; i++) {
|
|
int first = 0;
|
|
int last = 0;
|
|
char b_c = (i > 0) ? buffer[i - 1] : 0;
|
|
int n = -1;
|
|
|
|
b_c = tolower(b_c);
|
|
|
|
if(i == 0 || FT_IS_SKIPPABLE(buffer[i - 1])) first = 1;
|
|
if(i == (length - 1) || FT_IS_SKIPPABLE(buffer[i + 1])) last = 1;
|
|
|
|
if(ph[i * MAX_PPL] != FT_P_NONE) continue;
|
|
|
|
if(FT_IS_SKIPPABLE(buffer[i])) {
|
|
ph[i * MAX_PPL + 0] = FT_P_SPACE;
|
|
|
|
n = 0;
|
|
} else if((n = p_gen_4(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
} else if((n = p_silent_e(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
} else if((n = p_special_2(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
} else if((n = p_gen_2(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
} else if((n = p_vowel_2(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
} else if((n = p_soft(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
} else if((n = p_special_1(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
} else if((n = p_gen_1(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
} else if((n = p_vowel_1(ph, i, buffer, length, first, last, b_c)) != -1) {
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if(n == -1) fprintf(stderr, "cannot find phoneme for '%c', character behind was '%c'\n", buffer[i], b_c);
|
|
#endif
|
|
|
|
if(n != -1) i += n;
|
|
}
|
|
|
|
#define SYNTH \
|
|
if(ph[i] != FT_P_NONE && ph[i] != FT_P_IGNORE) { \
|
|
ft_synth_main(handle, ph[i]); \
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "phoneme of \"");
|
|
fwrite(buffer, length, 1, stderr);
|
|
fprintf(stderr, "\":");
|
|
for(i = 0; i < length * MAX_PPL; i++) {
|
|
if(ph[i] == FT_P_NONE) continue;
|
|
|
|
fprintf(stderr, " %s", phon_str[ph[i]]);
|
|
fflush(stderr);
|
|
|
|
SYNTH;
|
|
}
|
|
fprintf(stderr, "\n");
|
|
#else
|
|
for(i = 0; i < length * MAX_PPL; i++) {
|
|
SYNTH;
|
|
}
|
|
#endif
|
|
|
|
#undef SYNTH
|
|
|
|
free(ph);
|
|
}
|