miércoles, 27 de febrero de 2013

Instalando un sintetizador de voz en castellano en Debian/Ubuntu (TTS - Text To Speech)

Me ha surgido la necesidad de un sintetizador de voz en castellano para generar voces con el contenido de un mensaje de alerta en nuestros servidores Ubuntu Server (en proceso de migración a Debian).

He decidido usar "festival", haciendo uso del paquete de voz en castellano "festvox-ellpc11k". El proceso de instalación es muy sencillo:

apt-get install festival festvox-ellpc11k

He visitado bastantes páginas de internet que recomiendan no usar "text2wave" para generar la voz por no poder cambiar de lenguaje (!) por lo que se complican la vida agregando un archivo ".festivalrc" que contiene el siguiente texto:

(Parameter.set 'Audio_Method 'Audio_Command)
(Parameter.set 'Audio_Command "cat $FILE > foo.raw")

Para generar un archivo de sonido necesitamos dos pasos:

  • Generar el archivo RAW:
    iconv -f utf-8 -t iso-8859-1 < archivo.txt | festival --language spanish --tts
  • Convertir el archivo RAW en WAV:
    sox -c 1 -r 11025 -s -2 foo.raw salida.wav

No digo que esté mal hacerlo así, pero tiene el inconveniente de no funcionar de manera correcta si realizamos dos conversiones paralelas. Ambas trabajarán en el archivo foo.raw, perdiéndose una de las dos conversiones de texto a voz.

Finalmente, investigando un poco, he preferido "forzar" la configuración de idioma pasándosela como parámentro a "text2wave" como comando LISP que se evaluará previo a la conversión.

Creando un script (llamado "texto_a_voz.sh", por ejemplo) con el siguiente contenido nos facilitará la tarea enormemente:

#!/bin/bash
/usr/bin/iconv -f utf-8 -t iso-8859-1 | /usr/bin/text2wave -eval "(language_castillian_spanish)"

Bastará con ejecutar la siguiente línea para, a partir de un archivo de texto de entrada en formato UTF-8, generar un archivo WAV de salida:

texto_a_voz.sh < archivo.txt > salida.wav

Si deseáramos pasar el archivo a MP3, por ejemplo, bastaría con agregar a la cadena "lame", con la opción "-S" si no queremos mensajes (modo silencioso), a la línea del comando:

texto_a_voz.sh < archivo.txt | lame -S - salida.mp3

Espero que os sea de utilidad.

jueves, 14 de febrero de 2013

Particionando tablas en Zabbix 2.0

Tras usar durante varios años en producción Zabbix 1.8 estamos migrando la plataforma a Zabbix 2.0 para realizar una limpieza de equipos, refinado de plantillas, etc.

Una de las cosas que deben hacerse para mejorar el rendimiento de la base de datos es deshabilitar el housekeeping integrado en Zabbix y realizar el borrado de datos usando particiones en las tablas históricas.

Si usamos una guía genérica de particionamiento de tablas basado en Zabbix 1.8 no nos funcionará debido al uso de claves foráneas en algunas tablas. Por desgracia, a día de hoy, MySQL (ni Percona) soporta claves foráneas al mismo tiempo que particionamiento de tablas, por lo que la solución que propondremos aquí eliminará de forma permanente dichas relaciones.

Primer paso: Eliminar claves foráneas

# Tabla "acknowledges"
ALTER TABLE `acknowledges`
 DROP FOREIGN KEY `c_acknowledges_2`,
 DROP FOREIGN KEY `c_acknowledges_1`;

# Tabla "alerts"
ALTER TABLE `alerts`
 DROP FOREIGN KEY `c_alerts_4`,
 DROP FOREIGN KEY `c_alerts_1`,
 DROP FOREIGN KEY `c_alerts_2`,
 DROP FOREIGN KEY `c_alerts_3`;

# Tabla "auditlog"
ALTER TABLE `auditlog`
 DROP FOREIGN KEY `c_auditlog_1`;

# Tabla "auditlog_details"
ALTER TABLE `auditlog_details`
 DROP FOREIGN KEY `c_auditlog_details_1`;

# Tabla "service_alarms"
ALTER TABLE `service_alarms`
 DROP FOREIGN KEY `c_service_alarms_1`;

Segundo paso: Cambio de claves primarias

ALTER TABLE `acknowledges` DROP PRIMARY KEY, ADD KEY `acknowledgedid` (`acknowledgeid`);
ALTER TABLE `alerts` DROP PRIMARY KEY, ADD KEY `alertid` (`alertid`);
ALTER TABLE `auditlog` DROP PRIMARY KEY, ADD KEY `auditid` (`auditid`);
ALTER TABLE `events` DROP PRIMARY KEY, ADD KEY `eventid` (`eventid`);
ALTER TABLE `service_alarms` DROP PRIMARY KEY, ADD KEY `servicealarmid` (`servicealarmid`);
ALTER TABLE `history_log` DROP PRIMARY KEY, ADD PRIMARY KEY (`itemid`,`id`,`clock`);
ALTER TABLE `history_log` DROP KEY `history_log_2`;
ALTER TABLE `history_text` DROP PRIMARY KEY, ADD PRIMARY KEY (`itemid`,`id`,`clock`);
ALTER TABLE `history_text` DROP KEY `history_text_2`;

Tercer paso: Obtener fechas iniciales para particiones

SET @SEMANA= DATE_ADD(NOW(), INTERVAL 1 WEEK);
SET @F_SEMANA = DATE_FORMAT(@SEMANA, '%Y-%m-%d');
SET @P_SEMANA= CONCAT('p', DATE_FORMAT(@SEMANA, '%x%v'));
SELECT @F_SEMANA, @P_SEMANA;
SET @DIA= DATE_ADD(NOW(), INTERVAL 1 DAY);
SET @F_DIA = DATE_FORMAT(@DIA, '%Y-%m-%d');
SET @P_DIA= CONCAT('p', DATE_FORMAT(@DIA, '%Y%m%d'));
SELECT @F_DIA, @P_DIA;

Cuarto paso: Crear particiones semanales

# Formato de fecha PHP "oW"/'%x%v'
ALTER TABLE `acknowledges` PARTITION BY RANGE( clock ) ( PARTITION pYYYYss VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `alerts` PARTITION BY RANGE( clock ) ( PARTITION pYYYYss VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `events` PARTITION BY RANGE( clock ) ( PARTITION pYYYYss VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `auditlog` PARTITION BY RANGE( clock ) ( PARTITION pYYYYss VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `events` PARTITION BY RANGE( clock ) ( PARTITION pYYYYss VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `service_alarms` PARTITION BY RANGE( clock ) ( PARTITION pYYYYss VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `trends` PARTITION BY RANGE( clock ) ( PARTITION pYYYYss VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `trends_uint` PARTITION BY RANGE( clock ) ( PARTITION pYYYYss VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );

ALTER TABLE `acknowledges` PARTITION BY RANGE( clock ) ( PARTITION p201308 VALUES LESS THAN (UNIX_TIMESTAMP("2013-02-21 00:00:00")) );

Quinto paso: Crear particiones diarias

ALTER TABLE `history` PARTITION BY RANGE( clock ) ( PARTITION pYYYYmmdd VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `history_log` PARTITION BY RANGE( clock ) ( PARTITION pYYYYmmdd VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `history_str` PARTITION BY RANGE( clock ) ( PARTITION pYYYYmmdd VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `history_text` PARTITION BY RANGE( clock ) ( PARTITION pYYYYmmdd VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );
ALTER TABLE `history_uint` PARTITION BY RANGE( clock ) ( PARTITION pYYYYmmdd VALUES LESS THAN (UNIX_TIMESTAMP("XXXXXXXXXX 00:00:00")) );

ALTER TABLE `history` PARTITION BY RANGE( clock ) ( PARTITION p20130215 VALUES LESS THAN (UNIX_TIMESTAMP("2013-02-15 00:00:00")) );