Package Exports
- fonema
Readme
fonema 👄
Ultra-minimal Spanish text cleaning library for TTS. 100% Effect-TS, ESM-only, zero dependencies. Made by @blissito
Installation
npm install fonema effectAPI
cleanTextForTTS(text: string)
Main function that applies all Spanish text cleaning transformations in a single pipeline.
Returns: Effect<string, TextCleaningError>
Utility Functions
convertSpanishNumber(num: number): string- Convert numbers to Spanish wordsexpandSpanishAbbreviation(abbrev: string): string- Expand Spanish abbreviations
Features
- Numbers:
1,234→"mil doscientos treinta y cuatro" - Ordinals:
1º→"primero",2ª→"segunda" - Dates:
15/03/2024→"quince de marzo de dos mil veinticuatro" - Abbreviations:
Dr.→"Doctor",etc.→"etcétera" - Percentages:
25%→"veinticinco por ciento" - Code blocks: Removes ```blocks, preserves inline
code - URLs/emails: Complete removal
- Punctuation: RAE-compliant normalization
Inicio Rápido
import { cleanTextForTTS } from "fonema";
import { Effect } from "effect";
// Ejemplo que demuestra todas las capacidades de limpieza
const text = `
El Dr. GarcÃa, experto en IA y PLN, ha estado utilizando fonema v1.2.3 en su investigación durante 5 años.
Nació el 15/03/1980 y atiende en C/ Mayor, 123, 2ºB.
Contacto: dr.garcia@clinica.com | https://drgarcia.es
Su último estudio muestra una mejora del 75% en la naturalidad del TTS usando la normalización de texto de fonema:
\`\`\`typescript
// Antes: "El Dr. GarcÃa atiende en C/ Mayor, 123"
// Después de fonema: "El Doctor GarcÃa atiende en Calle Mayor, ciento veintitrés"
const texto = "El Dr. GarcÃa atiende en C/ Mayor, 123";
const textoLimpio = await Effect.runPromise(cleanTextForTTS(texto));
\`\`\`
¡Agenda tu cita al 555-123-4567!`;
const programa = cleanTextForTTS(text);
Effect.runSync(programa);
/* →
"El Doctor GarcÃa, experto en I A y P L N, ha estado utilizando fonema v uno punto dos punto tres en su investigación durante cinco años.
Nació el quince de marzo de mil novecientos ochenta y atiende en Calle Mayor, ciento veintitrés, segundo B.
Contacto:
Su último estudio muestra una mejora del setenta y cinco por ciento en la naturalidad del T T S usando la normalización de texto de fonema:
¡Agenda tu cita al cinco cinco cinco, uno veintitrés, cuarenta y cinco, sesenta y siete!"
*/¿Qué hizo fonema?
- Abreviaturas:
Dr.→Doctor - Fechas:
15/03/1980→quince de marzo de mil novecientos ochenta - Direcciones:
C/ Mayor→Calle Mayor - Números:
123→ciento veintitrés1.2.3→uno punto dos punto tres5→cinco
- Porcentajes:
75%→setenta y cinco por ciento - Eliminó:
- Email:
dr.garcia@clinica.com - URL:
https://drgarcia.es - Bloque de código completo
- Email:
- Números de teléfono:
555-123-4567→cinco cinco cinco, uno veintitrés, cuarenta y cinco, sesenta y siete - Formato de piso:
2ºB→segundo B - Versiones:
v1.2.3→v uno punto dos punto tres - Abreviaturas técnicas:
IA→I A,PLN→P L N,TTS→T T S
Examples
Standalone Usage
import { cleanTextForTTS, convertSpanishNumber } from "fonema";
import { Effect } from "effect";
// Number conversion
convertSpanishNumber(1234); // → "mil doscientos treinta y cuatro"
// Full text cleaning
const program = Effect.gen(function* () {
const result = yield* cleanTextForTTS(
"El 15/03/2024 el Dr. Smith presentó el 50% del proyecto."
);
console.log(result);
// → "El quince de marzo de dos mil veinticuatro el Doctor Smith presentó el cincuenta por ciento del proyecto."
});
Effect.runSync(program);Error Handling
import { cleanTextForTTS } from "fonema";
import { Effect } from "effect";
const program = cleanTextForTTS("Some text").pipe(
Effect.catchAll((error) => Effect.succeed(`Fallback: ${error.message}`))
);
Effect.runSync(program);TTS Integration Examples
Google Cloud Text-to-Speech
import { cleanTextForTTS } from "fonema";
import { Effect } from "effect";
import { TextToSpeechClient } from "@google-cloud/text-to-speech";
const client = new TextToSpeechClient();
const synthesizeSpanishText = (text: string) =>
Effect.gen(function* () {
// Clean text with fonema
const cleanedText = yield* cleanTextForTTS(text);
// Google TTS request
const [response] = yield* Effect.tryPromise(() =>
client.synthesizeSpeech({
input: { text: cleanedText },
voice: {
languageCode: "es-ES",
name: "es-ES-Neural2-A",
},
audioConfig: { audioEncoding: "MP3" },
})
);
return response.audioContent;
});
// Usage
const program = synthesizeSpanishText(
"El Dr. GarcÃa tiene 25 años y nació el 15/03/1998."
);
Effect.runPromise(program).then((audioBuffer) => {
// Handle audio buffer
});ElevenLabs Integration
import { cleanTextForTTS } from "fonema";
import { Effect } from "effect";
const elevenLabsTTS = (text: string, voiceId: string) =>
Effect.gen(function* () {
const cleanedText = yield* cleanTextForTTS(text);
const response = yield* Effect.tryPromise(() =>
fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, {
method: "POST",
headers: {
Accept: "audio/mpeg",
"Content-Type": "application/json",
"xi-api-key": process.env.ELEVENLABS_API_KEY!,
},
body: JSON.stringify({
text: cleanedText,
model_id: "eleven_multilingual_v2",
voice_settings: {
stability: 0.5,
similarity_boost: 0.5,
},
}),
})
);
return yield* Effect.tryPromise(() => response.arrayBuffer());
});OpenAI TTS Integration
import { cleanTextForTTS } from "fonema";
import { Effect } from "effect";
import OpenAI from "openai";
const openai = new OpenAI();
const openAITTS = (text: string) =>
Effect.gen(function* () {
const cleanedText = yield* cleanTextForTTS(text);
const mp3 = yield* Effect.tryPromise(() =>
openai.audio.speech.create({
model: "tts-1",
voice: "nova",
input: cleanedText,
})
);
return yield* Effect.tryPromise(() => mp3.arrayBuffer());
});Generic TTS Service Integration
import { cleanTextForTTS } from "fonema";
import { Effect } from "effect";
interface TTSService {
synthesize(text: string, options: any): Promise<ArrayBuffer>;
}
const createTTSPipeline =
(service: TTSService) => (text: string, options: any) =>
Effect.gen(function* () {
// Always clean Spanish text first
const cleanedText = yield* cleanTextForTTS(text);
// Then pass to any TTS service
return yield* Effect.tryPromise(() =>
service.synthesize(cleanedText, options)
);
});
// Usage with any TTS service
const myTTSPipeline = createTTSPipeline(myTTSService);
const program = myTTSPipeline("Text with números 123 and Dr. abbreviations", {
voice: "spanish-voice",
speed: 1.0,
});License
MIT Fixtergeek.com