fishtalk/src/phon.c
2026-05-23 14:08:06 +09:00

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