tts-server: normalizza i numeri interi 5+ cifre in parole italiane (Qwen3-TTS li leggeva cifra per cifra)
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
// text-normalize.h: normalizzazione testo per TTS.
|
||||
// Qwen3-TTS legge i numeri interi di 5+ cifre cifra per cifra
|
||||
// ("12345" -> "un, due, tre, quattro, cinque"). Questa funzione li
|
||||
// converte in parole italiane prima della sintesi.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace text_normalize {
|
||||
|
||||
static bool is_alnum_utf8(char c) {
|
||||
unsigned char u = (unsigned char) c;
|
||||
return (u >= '0' && u <= '9') || (u >= 'A' && u <= 'Z') || (u >= 'a' && u <= 'z') || u >= 0x80;
|
||||
}
|
||||
|
||||
// Converte un intero (0..999.999.999.999) in parole italiane.
|
||||
static std::string number_to_words(uint64_t n) {
|
||||
if (n == 0) return "zero";
|
||||
|
||||
static const char * units[] = {"", "uno", "due", "tre", "quattro", "cinque", "sei", "sette", "otto", "nove"};
|
||||
static const char * teens[] = {"dieci", "undici", "dodici", "tredici", "quattordici", "quindici", "sedici",
|
||||
"diciassette", "diciotto", "diciannove"};
|
||||
static const char * tens[] = {"", "", "venti", "trenta", "quaranta", "cinquanta", "sessanta", "settanta",
|
||||
"ottanta", "novanta"};
|
||||
|
||||
// Converte un gruppo di 3 cifre (0-999).
|
||||
auto group3 = [&](int g) -> std::string {
|
||||
if (g == 0) return "";
|
||||
std::string out;
|
||||
int h = g / 100, rest = g % 100;
|
||||
if (h > 0) {
|
||||
out += (h == 1) ? "cento" : (std::string(units[h]) + "cento");
|
||||
}
|
||||
if (rest >= 10 && rest < 20) {
|
||||
out += teens[rest - 10];
|
||||
} else {
|
||||
int t = rest / 10, u = rest % 10;
|
||||
if (t > 1) {
|
||||
std::string dec = tens[t];
|
||||
if (u == 1 || u == 8) {
|
||||
dec.pop_back(); // venti+uno=ventuno, venti+otto=ventotto
|
||||
out += dec + units[u];
|
||||
} else if (u == 3) {
|
||||
out += dec + "tr\xc3\xa9"; // ventitré (accento)
|
||||
} else if (u > 0) {
|
||||
out += dec + units[u];
|
||||
} else {
|
||||
out += dec;
|
||||
}
|
||||
} else if (t == 1) {
|
||||
out += teens[u];
|
||||
} else if (u > 0) {
|
||||
out += units[u];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
static const char * scale_singular[] = {"", "mille", "un milione", "un miliardo"};
|
||||
static const char * scale_plural[] = {"", "mila", "milioni", "miliardi"};
|
||||
|
||||
int groups[4] = {0, 0, 0, 0};
|
||||
uint64_t v = n;
|
||||
for (int i = 0; i < 4 && v > 0; i++) {
|
||||
groups[i] = (int) (v % 1000);
|
||||
v /= 1000;
|
||||
}
|
||||
|
||||
std::string out;
|
||||
bool first = true;
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
int g = groups[i];
|
||||
if (g == 0) continue;
|
||||
std::string words = group3(g);
|
||||
if (i > 0) {
|
||||
if (g == 1) {
|
||||
words = scale_singular[i];
|
||||
} else {
|
||||
words += " " + std::string(scale_plural[i]);
|
||||
}
|
||||
}
|
||||
if (!first) out += " ";
|
||||
out += words;
|
||||
first = false;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Normalizza i numeri interi di 5+ cifre in parole italiane.
|
||||
// Non tocca: date, orari, decimali, codici, numeri < 5 cifre, numeri > 12 cifre.
|
||||
static void normalize_numbers(std::string & s) {
|
||||
std::string out;
|
||||
out.reserve(s.size() + 32);
|
||||
size_t i = 0;
|
||||
const size_t n = s.size();
|
||||
while (i < n) {
|
||||
unsigned char c = (unsigned char) s[i];
|
||||
if (c >= '0' && c <= '9') {
|
||||
size_t j = i;
|
||||
while (j < n && s[j] >= '0' && s[j] <= '9') j++;
|
||||
size_t len = j - i;
|
||||
bool left_ok = (i == 0) || !is_alnum_utf8(s[i - 1]);
|
||||
bool right_ok = (j == n) || !is_alnum_utf8(s[j]);
|
||||
if (len >= 5 && len <= 12 && left_ok && right_ok) {
|
||||
uint64_t val = 0;
|
||||
for (size_t k = i; k < j; k++) {
|
||||
val = val * 10 + (uint64_t) (s[k] - '0');
|
||||
}
|
||||
out += number_to_words(val);
|
||||
} else {
|
||||
out.append(s, i, len);
|
||||
}
|
||||
i = j;
|
||||
} else {
|
||||
out += s[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
s.swap(out);
|
||||
}
|
||||
|
||||
} // namespace text_normalize
|
||||
Reference in New Issue
Block a user