Upload
vuongdang
View
213
Download
0
Embed Size (px)
Citation preview
Apendice B
Codigo en C++
Listados completos del codigo fuente del software desarrollado.
B.1. Common.h
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include <string>
#include <vector>
#include <fstream>
#include <limits>
#include <crtdbg.h>
typedef float real;
using namespace std;
// Numero pi
#define PI (real)3.1415926538
//! \brief Guarda una cadena de caracteres en un archivo.
181
182 APENDICE B. CODIGO EN C++
//!
//! @param s La cadena de caracteres.
//! @param f Flujo de datos de archivo.
void SaveString( string &s, fstream &f );
//! \brief Carga una cadena de caracteres de un archivo.
//!
//! @param s La cadena de caracteres.
//! @param f Flujo de datos de archivo.
void LoadString( string &s, fstream &f );
//! \brief Guarda un vector de enteros en un archivo.
//!
//! @param v El vector de enteros.
//! @param f Flujo de datos de archivo.
void SaveIVector( vector<int> &v, fstream &f );
//! \brief Carga un vector de enteros de un archivo.
//!
//! @param v El vector de enteros.
//! @param f Flujo de datos de archivo.
void LoadIVector( vector<int> &v, fstream &f );
#endif //__COMMON_H__
B.2. COMMON.CPP 183
B.2. Common.cpp
#include "Common.h"
void SaveString( string &s, fstream &f )
{
int n = s.length();
f.write( (char *)&n, sizeof(int) );
f.write( s.c_str(), n );
}
void LoadString( string &s, fstream &f )
{
int n;
f.read( (char *)&n, sizeof(int) );
char *cad = new char[n+1];
f.read( cad, n );
cad[n] = 0;
s = cad;
delete cad;
}
void SaveIVector( vector<int> &v, fstream &f )
{
int n = v.size();
f.write( (char *)&n, sizeof(int) );
for( int i = 0; i < n; i++ )
{
f.write( (char *)&v[i], sizeof(int) );
}
}
void LoadIVector( vector<int> &v, fstream &f )
{
v.clear();
int n, val;
f.read( (char *)&n, sizeof(int) );
for( int i = 0; i < n; i++ )
{
f.read( (char *)&val, sizeof(int) );
v.push_back( val );
184 APENDICE B. CODIGO EN C++
}
}
B.3. SIGNAL.H 185
B.3. Signal.h
#ifndef __SIGNAL_H__
#define __SIGNAL_H__
#include "Common.h"
#include <stdlib.h>
#include <math.h>
#include <memory.h>
class CSignal
{
//! \brief Puntero a la unica instancia de la clase.
//!
//! La clase sigue el patron \a singleton.
static CSignal *m_pInstance;
//! Se~nal acustica a analizar.
real *m_prSignal;
//! Numero de muestras en la se~nal.
int m_iNumMuestras;
//! Frecuencia de muestreo de la se~nal.
int m_iFrecuencia;
//! Longitud de la ventana de analisis.
int m_iLongVent;
//! Logaritmo en base 2 de la longitud de la ventana.
int m_iOrden;
//! Longitud de superposicion de las ventanas.
int m_iSupVent;
//! Array para la transformada en cada frame.
real **m_pprTransformada;
//! Numero de intervalos sobre los que se analiza la
//! transformada de Fourier.
int m_iNumFrames;
//! Array para almacenar los valores de la energıa de la se~nal.
real *m_prEnergia;
//! Arrays para almacenar los coeficientes cepstrales obtenidos.
real **m_pprCepstrales;
//! Numero de coeficientes cepstrales que se desean obtener.
int m_iNumCeps;
//! Vectores de caracterısticas que incluyen los coeficientes
186 APENDICE B. CODIGO EN C++
//! cepstrales y la energıa con sus derivadas.
real *m_prCarac;
//! \brief Determinar ancho de la ventana.
//!
//! Determina la longitud de ventana que se debe coger para la
//! frecuencia utilizada.
void SetLongVent();
//! \brief Transformada de Fourier.
//!
//! Calcula la transformada de Fourier del frame \a t.
//! @param t Intervalo en el que se tiene que calcular la
//! transformada de Fourier.
void Fourier( int t );
//! \brief Calculo de los coeficientes cepstrales.
//!
//! Calcula los coeficientes cepstrales del frame \a t.
//! @param t Intervalo en el que se tiene que calcular los
//! coeficientes cepstrales. Es requisito haber llamado antes
//! al metodo \a Fourier().
void Mfcc( int t );
//! \brief Calcula las derivadas de los coeficientes y la
//! energıa.
//!
//! Para calcular las derivadas y la energıa se debe haber
//! llamado antes a los metodos \a Fourier() y \a Mfcc().
void Derivadas();
//! \brief Realiza la normalizacion de la se~nal de audio.
void Normaliza();
//! \brief Escala la se~nal a todo el ancho de banda.
void Banda();
//! \brief Ventana de Hamming.
//!
//! @param n Indice del valor de la ventana de Hamming que se
//! quiere obtener.
B.3. SIGNAL.H 187
//! @return Devuelve el termino enesimo de una ventana de
//! Hamming de ancho igual al de los frames.
real W_Hamming( int n );
//! \brief Ventana de Hanning.
//!
//! @param n Indice del valor de la ventana de Hanning que se
//! quiere obtener.
//! @return Devuelve el termino enesimo de una ventana de
//! Hanning de ancho igual al de los frames.
real W_Hanning( int n );
//! \brief Ventana Rectangular.
//!
//! @param n Indice del valor de la ventana rectangular que se
//! quiere obtener.
//! @return Devuelve el termino enesimo de una ventana
//! rectangular de ancho igual al de los frames.
real W_Rectangular( int n );
//! \brief Constructor.
//!
//! El constructor de instancias de la clase es privado porque
//! esta sigue el patron \a singleton.
CSignal();
//! \brief Destructor.
//!
//! El destructor de instancias de la clase es privado porque
//! esta sigue el patron \a singleton.
~CSignal();
public:
//! \brief Obtencion de la instancia de la clase.
//!
//! La clase sigue el patron \a singleton y mediante este metodo
//! se obtiene la unica instancia de ella.
//! @return Devuelve el puntero a la instancia unica de la clase.
static CSignal *GetExtractor();
//! \brief Definicion de los datos del analisis.
188 APENDICE B. CODIGO EN C++
//!
//! Como la clase sigue el patron singleton, en cada uso de ella
//! hay que difinir los datos y parametros con que se trabaja.
//! @param sig Array de la se~nal que se quire analizar.
//! @param numM Numero de muestras en \a sig.
//! @param frec Frecuancia de muestreo d la se~nal introducida.
//! @see SetNumCeps()
void SetAnalisis( real *sig, int numM, int frec );
//! \brief Realiza el analisis de la se~nal.
//!
//! Tras determinar los datos del analisis se llama a este
//! metodo pa ordenar la realizacion del analisis.
void Analiza();
//! \brief Numero de frames de la se~nal.
//!
//! @return Devuelve el numero de frames en que se ha dividido
//! la se~nal ara su analisis.
inline int GetNumFrames()
{
return m_iNumFrames;
}
//! Obtencion del vector de caracterıasticas.
//!
//! @param f Indice del frame para el que se quiere el vector de
//! caracterısticas.
//! @return Puntero al primer elemento del vector de
//! coeficientes. El numero de elementos que se devuelve es
//! igual al numero de coeficientes cepstrales calculados mas
//! el valor de la energıa y sus dos primeras derivadas.
real *GetCaract( int f );
//! \brief Determinar el numero de coeficientes cepstrales que se
//! quiere obtener.
//!
//! @param nc Numero de coeficientes cepstrales que se deben
//! calcular por cada frame.
//! @see SetAnalisis()
inline void SetNumCeps( int nc )
B.3. SIGNAL.H 189
{
m_iNumCeps = nc;
}
//! \brief Se~nal en analisis.
//!
//! @return Puntero a la primera posicion de los datos de la
//! se~nal introducida.
inline real *GetSign()
{
return m_prSignal;
}
//! \brief Numero de muestras de la se~nal en analisis.
//!
//! @return Numero de muestras de la se~nal.
inline int GetNumMuestras()
{
return m_iNumMuestras;
}
//! \brief Determina la longitud de la ventana como la mınima.
void SetLongVentMin();
};
#endif //__SIGNAL_H__
190 APENDICE B. CODIGO EN C++
B.4. Signal.cpp
#include "Signal.h"
CSignal *CSignal::m_pInstance = NULL;
CSignal *CSignal::GetExtractor()
{
// Si no se ha creado ya la instancia de la clase se hace ahora.
if(!m_pInstance)
m_pInstance = new CSignal;
// Se devuelve la instancia de la clase.
return m_pInstance;
}
CSignal::CSignal()
{
// Inicializacion de parametros.
m_iNumMuestras = 0;
m_iFrecuencia = 0;
m_prSignal = NULL;
m_pprTransformada = NULL;
m_prEnergia = NULL;
m_pprCepstrales = NULL;
m_prCarac = NULL;
}
void CSignal::SetAnalisis( real *sig, int numM, int frec )
{
int i;
// Devolucion de la memoria reservada en usos anteriores.
if( m_prSignal )
delete m_prSignal;
if( m_prEnergia )
delete m_prEnergia;
if( m_prCarac )
delete m_prCarac;
if( m_pprTransformada )
B.4. SIGNAL.CPP 191
{
for( i = 0; i < m_iNumFrames; i++ )
delete m_pprTransformada[i];
delete m_pprTransformada;
}
if(m_pprCepstrales)
{
for( i = 0; i < m_iNumFrames * 3; i++ )
delete m_pprCepstrales[i];
delete m_pprCepstrales;
}
// Array para la se~nal
m_prSignal = new real[numM];
memcpy( m_prSignal, sig, sizeof(real) * numM );
m_iNumMuestras = numM;
m_iFrecuencia = frec;
// Determina la longitud del frame.
SetLongVent();
m_pprTransformada = NULL;
m_prEnergia = NULL;
m_pprCepstrales = NULL;
m_prCarac = NULL;
}
CSignal::~CSignal()
{
int i;
// Devolucion de la memoria reservada.
if(m_prSignal)
delete m_prSignal;
if( m_prEnergia )
delete m_prEnergia;
if( m_prCarac )
delete m_prCarac;
if( m_pprTransformada )
{
for( i = 0; i < m_iNumFrames; i++ )
192 APENDICE B. CODIGO EN C++
delete m_pprTransformada[i];
delete m_pprTransformada;
}
if(m_pprCepstrales)
{
for( i = 0; i < m_iNumFrames * 3; i++ )
delete m_pprCepstrales[i];
delete m_pprCepstrales;
}
}
void CSignal::SetLongVent()
{
m_iLongVent = int( 0.03f * (real)m_iFrecuencia );
if( m_iLongVent < 384 )
{
m_iLongVent = 256;
m_iOrden = 8;
m_iSupVent = 100;
}
else if( m_iLongVent < 768 )
{
m_iLongVent = 512;
m_iOrden = 9;
m_iSupVent = 200;
}
else
{
m_iLongVent = 1024;
m_iOrden = 10;
m_iSupVent = 400;
}
}
void CSignal::SetLongVentMin()
{
m_iLongVent = 256;
m_iOrden = 8;
m_iSupVent = 100;
}
B.4. SIGNAL.CPP 193
real CSignal::W_Hamming( int n )
{
if( n >= 0 && n < m_iLongVent )
return 0.54f - 0.46f * (real)cos( 2.0 * PI * (real)n
/ (real)( m_iLongVent - 1 ) );
else
return 0.0f;
}
real CSignal::W_Hanning( int n )
{
if( n >= 0 && n < m_iLongVent )
return 0.5f - 0.5f * (real)cos( 2.0 * PI * (real)n
/ (real)( m_iLongVent - 1 ) );
else
return 0.0f;
}
real CSignal::W_Rectangular( int n )
{
if( n >= 0 && n < m_iLongVent )
return 1.0f;
else
return 0.0f;
}
void CSignal::Analiza()
{
int i;
// Reinicializamos los arrays para la transformada de Fourier
// y la energıa.
if(m_pprTransformada)
{
for( i = 0; i < m_iNumFrames; i++ )
delete m_pprTransformada[i];
delete m_pprTransformada;
}
if(m_pprCepstrales)
{
194 APENDICE B. CODIGO EN C++
for( i = 0; i < m_iNumFrames * 3; i++ )
delete m_pprCepstrales[i];
delete m_pprCepstrales;
}
if(m_prEnergia)
delete m_prEnergia;
m_prEnergia = NULL;
m_pprTransformada = NULL;
// El numero de frames es el siguiente.
m_iNumFrames = ( m_iNumMuestras - m_iSupVent )
/ ( m_iLongVent - m_iSupVent );
// Inicializamos los arrays
m_pprTransformada = new real *[m_iNumFrames];
m_pprCepstrales = new real *[m_iNumFrames * 3];
m_prEnergia = new real[m_iNumFrames * 3];
for( i = 0; i < m_iNumFrames * 3; i++ )
{
if( i < m_iNumFrames )
m_pprTransformada[i] = new real[m_iLongVent / 2];
m_pprCepstrales[i] = new real[m_iNumCeps];
}
// Normalizamos la se~nal.
Normaliza();
// Ancho de banda entre -1 y 1
Banda();
// Calculamos la transformada para cada frame.
for( i = 0; i < m_iNumFrames; i++ )
{
Fourier( i );
}
// Calculamos los coeficientes cepstrales para cada frame.
for( i = 0; i < m_iNumFrames; i++ )
{
Mfcc( i );
B.4. SIGNAL.CPP 195
}
Derivadas();
}
void CSignal::Fourier( int tf )
{
real *TF = m_pprTransformada[tf];
int c = tf * (m_iLongVent - m_iSupVent);
// Array para la parte real de la transformada
real *SigR = new real[m_iLongVent];
// Array para la parte imaginaria de la transformada
real *SigI = new real[m_iLongVent];
// Variables auxiliares.
real t, theta, wr, wi;
real arg, dwr, dwi, tr, ti;
int i, j, k, n2, n, z;
// Introducimos en el array de la parte real la muestra pasada
// por la ventana de Hamming y el de la parte imaginaria lo
// iniciamos a cero.
for( i = 0; i < m_iLongVent; i++ )
{
SigR[i] = W_Hamming(i) * m_prSignal[c + i];
}
memset( SigI, 0, sizeof(real) * m_iLongVent );
theta = PI;
// Shuffle
n2 = (1<<m_iOrden)>>1;
j = 1;
for( i = 1; i < m_iLongVent; i++ )
{
if( i < j )
{
t = SigR[i-1];
SigR[i-1] = SigR[j-1];
196 APENDICE B. CODIGO EN C++
SigR[j-1] = t;
}
k = n2;
while( k < j )
{
j -= k;
k >>= 1;
}
j += k;
}
// Transformada rapida de Fourier
n2 = 1;
for( i = 1; i <= m_iOrden; i++ )
{
n = n2;
n2 += n2;
wr = 1.0f;
wi = 0.0f;
arg = theta / (real)n;
dwr = (real)cos( arg );
dwi = (real)sin( arg );
for( j = 1; j < n; j++ )
{
for( k = j; k <= ( m_iLongVent ); k += n2 )
{
z = k + n;
tr = SigR[z-1] * wr + SigI[z-1] * wi;
ti = SigI[z-1] * wr - SigR[z-1] * wi;
SigR[z-1] = SigR[k-1] - tr;
SigI[z-1] = SigI[k-1] - ti;
SigR[k-1] += tr;
SigI[k-1] += ti;
}
tr = wr * dwr - wi * dwi;
wi = wi * dwr + wr * dwi;
wr = tr;
}
B.4. SIGNAL.CPP 197
}
// Hasta aquı tenemos parte real e imaginaria de la transformada
// de Fourier.
// Cogemos la mitad de los elementos para obtener la magnitud
// de la transformada.
m_prEnergia[tf] = 0.0f;
for( i = 0; i < m_iLongVent/2; i++ )
{
SigR[i] = SigR[i] * SigR[i] / m_iLongVent;
SigI[i] = SigI[i] * SigI[i] / m_iLongVent;
TF[i] = (real)sqrt( SigR[i] + SigI[i] );
m_prEnergia[tf] += ( SigR[i] + SigI[i] );
}
m_prEnergia[tf] = (real)log( 2 * sqrt( 2 * m_prEnergia[tf] ) );
delete SigR;
delete SigI;
}
void CSignal::Mfcc( int t )
{
real *TF = m_pprTransformada[t];
real *MFCC = m_pprCepstrales[t];
real factor = 1.2f;
real amplitud = (real)m_iFrecuencia / (real)m_iLongVent;
real amplitud_k = amplitud / 1000.0f;
real fdelta = amplitud_k / 10.0f;
int izq = 1;
int cen = izq + (int)fdelta;
int der = cen + (int)fdelta;
real a, b;
int j, k, nfiltros;
int n = 1;
198 APENDICE B. CODIGO EN C++
int i = 1;
real *filterbank = new real[ m_iLongVent/2 + 4 ];
while( n < amplitud_k )
{
filterbank[i-1] = 0;
b = 2.0f / (real)(( 2 + der - izq ) - 1);
for( j = izq; j <= der; j++ )
{
a = 1 - (real)fabs( (real)(cen - j) ) * b;
filterbank[i-1] += TF[j-1] * a;
}
i++;
n += (int)fdelta;
izq = n;
cen = izq + (int)fdelta;
der = cen + (int)fdelta;
}
while( der < m_iLongVent / 2 )
{
filterbank[i-1] = 0;
b = 2.0f / (real)(( 2 + der - izq ) - 1);
for( j = izq; j <= der; j++ )
{
a = 1 - (real)fabs( (real)(cen - j) ) * b;
filterbank[i-1] += TF[j-1] * a;
}
i += 1;
fdelta *= factor;
n += (int)fdelta;
izq = n;
cen = izq + (int)fdelta;
der = cen + (int)fdelta;
}
B.4. SIGNAL.CPP 199
nfiltros = i - 1;
for( i = 0; i < nfiltros; i++ )
{
if( filterbank[i] >= 0 )
filterbank[i] = 20.0f * (real)log10( filterbank[i] );
else
filterbank[i] = 0.0f;
}
for( i = 0; i < m_iNumCeps; i ++ )
{
MFCC[i] = 0.0f;
for( k = 0; k < nfiltros; k++ )
{
a = ((real)k + 0.5f) * ( PI * (i + 1) );
a /= (real)nfiltros;
a = (real)cos( a );
MFCC[i] += filterbank[k] * a;
}
}
delete filterbank;
}
void CSignal::Derivadas()
{
int i, j;
real **DC = m_pprCepstrales + m_iNumFrames;
real **DDC = m_pprCepstrales + m_iNumFrames * 2;
real *DE = m_prEnergia + m_iNumFrames;
real *DDE = m_prEnergia + m_iNumFrames * 2;
for( j = 0; j < m_iNumCeps; j++ )
{
DC[0][j] = 0.0f;
DDC[0][j] = 0.0f;
200 APENDICE B. CODIGO EN C++
DE[0] = 0.0f;
DDE[0] = 0.0f;
for( i = 1; i < m_iNumFrames; i++ )
{
DC[i][j] = m_pprCepstrales[i][j]
- m_pprCepstrales[i-1][j];
DDC[i][j] = DC[i][j] - DC[i-1][j];
DE[i] = m_prEnergia[i] - m_prEnergia[i - 1];
DDE[i] = DE[i] - DE[i-1];
}
}
}
real *CSignal::GetCaract( int f )
{
if( m_prCarac )
delete m_prCarac;
// Cada vector de caracterısticas incluye los ’m_iNumCeps’
// coeficientes cepstrales, la energıa y sus dos primeras
// derivadas.
m_prCarac = new real[m_iNumCeps + 3];
memcpy( m_prCarac, m_pprCepstrales[f],
sizeof(real)*m_iNumCeps );
m_prCarac[m_iNumCeps] = m_prEnergia[f];
m_prCarac[m_iNumCeps + 1] = m_prEnergia[f + m_iNumFrames];
m_prCarac[m_iNumCeps + 2] = m_prEnergia[f + 2 * m_iNumFrames];
return m_prCarac;
}
void CSignal::Normaliza()
{
real media = 0;
real varianza = 0;
int i;
B.4. SIGNAL.CPP 201
// Se obtiene la media de la se~nal
for( i = 0; i < m_iNumMuestras; i++ )
{
media += m_prSignal[i] / (real)m_iNumMuestras;
}
// Se obtiene la varianza de la se~nal
for( i = 0; i < m_iNumMuestras; i++ )
{
varianza += (m_prSignal[i] - media)*(m_prSignal[i] - media)
/ (real)(m_iNumMuestras-1);
}
// Se normaliza la se~nal
for( i = 0; i < m_iNumMuestras; i++ )
{
m_prSignal[i] = (m_prSignal[i] - media) / varianza;
}
}
void CSignal::Banda()
{
real max = 0;
int i;
// Obtiene el valor maximo de la se~nal normalizada
for( i = 0; i < m_iNumMuestras; i++ )
{
real a = (real)fabs(m_prSignal[i]);
if( max < a )
max = a;
}
// Divide cada valor por el maximo
for( i = 0; i < m_iNumMuestras; i++ )
{
m_prSignal[i] /= max;
}
}
202 APENDICE B. CODIGO EN C++
B.5. HMM.h
#ifndef __HMM_H__
#define __HMM_H__
#include "Common.h"
//! \brief Clase que representa un Modelo Oculto de Markov.
class HMM
{
//! \brief Tipo de modelo oculto de Markov.
//!
//! La diferenciacion entre distintos modelos no esta
//! implementada.
int m_nTipo;
//! \brief Numero de estados del modelo.
//!
//! Seran 3 por defecto.
int m_nNumEstados;
//! \brief Tama~no del conjunto de observacion
//!
//! Numero de elementos del conjunto de sımbolos observables.
int m_nNumElementos;
//! \brief Probabilidad de comienzo de la secuencia para cada
//! estado.
//!
//! Por defecto sera nulo para todos los estados excepto para
//! el primero. Solo se estimaran nuevo valores si se especifica
//! en el tipo de cadena. La longitud del vector sera igual al
//! numero de elementos del sistema.
real *m_prPi;
//! \brief Matriz de probabilidad de transicion entre estados.
//!
//! \f$a_{ij}\f$ = Probabilidad de pasar al estado \a i desde el
//! estado \a j. Segun el tipo de modelo algunas transiciones
//! estaran prohibidas por defecto. Por ejemplo en el caso de
//! cadenas sin retroceso de estados o sin saltos.
B.5. HMM.H 203
real *m_prA;
//! \brief Probabilidad de emision de sımbolos.
//!
//! \f$b_j(k)\f$ = Probabilidad de que se emita el sımbolo
//! \f$v_k\f$ estando en el estado \a j.
real *m_prB;
//! Secuencia de observacion en estudio.
vector<int> m_Sec;
//! \brief Probabilidad de la secuencia de observacion parcial.
//!
//! \f$\alpha_t(i)\f$. Probabilidad de la secuencia de
//! observacion en estudio supuesta hasta el instante \a t y de
//! que en dicho instante el estado sea \a i.
real *m_prAlfa;
//! \brief Variable de retroceso.
//!
//! \f$\beta_t(i)\f$. Probabilidad de la secuencia de
//! observacion desde \a t+1 a \a T dado el estado \a i en el
//! instante \a t.
real *m_prBeta;
//! \brief Algoritmo de avance.
//!
//! Calcula \f$\alpha_t(i)\f$ mediante el algoritmo de avance.
//! Reserva el espacio necesario y almacena el puntero en
//! \a m_prAlfa.
void CalcAlfa();
//! \brief Algoritmo de retroceso.
//!
//! Calcula \f$\beta_t(i)\f$ mediante el algoritmo de retroceso.
//! Reserva el espacio necesario y almacena el puntero en
//! \a m_prBeta.
void CalcBeta();
//! \brief Calculo de \f$\gamma_t(i)\f$.
//!
204 APENDICE B. CODIGO EN C++
//! Devuelve los valores de \a gamma en una fila para cada
//! estado \a i y para cada instante \a t por columnas. Si \a xi
//! toma valor NULL llama a CalcXi().
//! @param gamma Posicion de memoria donde escribir los valores
//! de \f$\gamma_t(i)\f$. El espacio debe estar reservado antes
//! de la llamada a la funcion y debe ser de tama~no
//! \a m_nNumEstados * \a max_T.
//! @param max_T Numero de elementos de la mas larga de las
//! secuencias de observacion.
//! @param xi Resultado del calculo de \f$\xi_t(i,j)\f$. Si es NULL
//! (valor por defecto) llama al metodo CalcXi().
void CalcGamma( real *gamma, int max_T, real *xi = NULL );
//! \brief Calculo de \f$\xi_t(i,j)\f$
//!
//! Devuelve los valores de \a xi en una matriz para cada
//! elemeto \a j, una fila para cada estado \a i y con cada
//! instante \a t por columnas; de modo que
//! \f$\xi_t(i,j)\f$ = xi[(i*m_nNumEstados + j)*max_T + t]
//! Si no se han calculado ya los valores \f$\alpha_t(i)\f$ y
//! \f$\beta_t(i)\f$, llama a los metodos CalcAlfa() y
//! CalcBeta().
//! @param xi Posicion de memoria donde escribir los valores de
//! \f$\xi_t(i,j)\f$. El espacio debe estar reservado antes de
//! la llamada a la funcion y debe ser de tama~no
//! \a m_nNumEstados * \a m_nNumElementos * \a max_T.
//! @param max_T Numero de elementos de la mas larga de las
//! secuencias de observacion.
void CalcXi( real *xi, int max_T );
//! \brief Algoritmo de Viterbi.
//!
//! @return Devuelve en un vector de enteros la secuencia de
//! estados optima para la observacion dada.
vector<int> Viterbi();
//! \brief Metodo de reestimacion de Baum-Welch.
//!
//! Esta funcion realiza un paso del metodo de reestimacion
//! de Baum-Welch.
void Paso_Baum_Welch();
B.5. HMM.H 205
//! \brief Inicializa las matrices caracterısticas del modelo.
//!
//! Con la implementacion de los distintos tipos de modelos
//! ocultos de Markov variara la inicializacion de las matrices
//! caracterısticas.
void Inicializar();
//! \brief Devuelve la probabilidad de ocurrencia de la
//! secuencia de observacion.
//!
//! @return La probabilidad de ocurrecia de la secuencia en
//! /a m_Sec.
real Probabilidad();
//! \brief Determina la secuencia de observacion para estudiar
//! con el modelo.
void Observacion( vector<int> &sec );
public:
//! \brief Constructor.
//!
//! @param n Numero de estados del modelo.
//! @param m Numero de elementos del espacio observables.
//! @param tipo Tipo del modelo. Por defecto 0.
HMM( int n, int m, int tipo = 0 );
//! \brief Constructor vacio.
//!
//! Igual a HMM( 3, 16, 0 ).
HMM();
//! \brief Destructor.
~HMM();
//! \brief Probabilidad de una secuencia.
//!
//! Devuelve la probabilidad de ocurrencia de la secuencia
//! de observacion dada.
//! @param sec Vector de enteros que contiene la secuencia de
//! observacion.
206 APENDICE B. CODIGO EN C++
//! @return La probabilidad de dicha secuencia.
real Probabilidad( vector<int> &sec );
//! \brief Entrenamieno del modelo
//!
//! Entrena el modelo a partir de un conjunto de secuencias de
//! observacion
//! @param secuencias Vector de vectores de enteros conteniendo
//! las secuencias de observacion.
//! @see EntrenarSecuencia()
void EntrenarGrupo( vector< vector<int> > &secuencias );
//! \brief Entrenamiento del modelo.
//!
//! Entrena el modelo a partir de una unica secuencia de
//! observacion
//! Este metodo es solo util para pruebas y experientos porque
//! no realiza un entrenamiento adecuado de un modelo.
//! @param sec Vector de enteros que contiene la secuencia de
//! observacion.
//! @see EntrenarSecuencia()
void EntrenarSecuencia( vector<int> &sec );
//! \brief Guarda el modelo en el archivo especificado.
//!
//! @param filename Nombre del archivo con la ruta completa.
void Save( string &filename );
//! \brief Guarda el modelo en el archivo especificado.
//!
//! @param file ’Stream’ del archivo donde se quiere guardar el
//! modelo.
void Save( fstream &file );
//! \brief Carga el modelo desde el archivo especificado.
//!
//! @param filename Nombre del archivo con la ruta completa.
void Load( string &filename );
//! \brief Carga el modelo desde el archivo especificado.
//!
B.5. HMM.H 207
//! @param file Origen de datos del archivo del que se quiere
//! cargar el modelo.
void Load( fstream &file );
};
//! \brief Detecta si \a r es un numero real o si por el contrario
//! se ha producido un error en su obtencion.
//!
//! @param r Valor que se quiere comprobar.
//! @return \a true si \a r es un numero real o \a false en caso
//! contrario.
bool NotReal( real r );
#endif //__HMM_H__
208 APENDICE B. CODIGO EN C++
B.6. HMM.cpp
#include "HMM.h"
HMM::HMM( int n, int m, int tipo )
{
m_nNumEstados = n;
m_nNumElementos = m;
m_nTipo = tipo;
Inicializar();
}
HMM::HMM()
{
m_nNumEstados = 3;
m_nNumElementos = 16;
m_nTipo = 0;
Inicializar();
}
void HMM::Inicializar()
{
int i, j;
int longA = m_nNumEstados * m_nNumEstados;
int longB = m_nNumEstados * m_nNumElementos;
real estInv = 1.0 / (real)m_nNumEstados;
real elemInv = 1.0 / (real)m_nNumElementos;
// Inicializacion del modelo.
// De momento se ignoran los tipos de estos.
m_prA = new real[longA];
m_prB = new real[longB];
m_prPi = new real[m_nNumEstados];
// Matriz A de probabilidad uniforme.
for( i = 0; i < longA; i++ )
m_prA[i] = estInv;
B.6. HMM.CPP 209
// Matriz B de probabilidad uniforme.
for( i = 0; i < longB; i++ )
m_prB[i] = elemInv;
// Entrada en el modelo por el primer estado.
m_prPi[0] = 1.0;
for( i = 1; i < m_nNumEstados; i++ )
{
m_prPi[i] = 0.0;
}
// Puesta a NULL de las variables de avance y retroceso.
m_prAlfa = NULL;
m_prBeta = NULL;
}
HMM::~HMM()
{
if( m_prA )
delete m_prA;
if( m_prB )
delete m_prB;
if( m_prPi )
delete m_prPi;
if( m_prAlfa )
delete m_prAlfa;
if( m_prBeta )
delete m_prBeta;
}
void HMM::CalcAlfa()
{
int t, i, j, aux;
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
if( m_Sec.size() == 0 )
{
210 APENDICE B. CODIGO EN C++
return;
}
// Tama~no de la secencia.
int T = (int)m_Sec.size();
// Reserva de memoria para los vectores alfa.
if( m_prAlfa )
delete m_prAlfa;
m_prAlfa = new real[m_nNumEstados * T];
// Calculo de alfa_0[i].
for( i = 0; i < m_nNumEstados; i++ )
{
m_prAlfa[i*T] = m_prPi[i]
* m_prB[ i*m_nNumElementos + m_Sec[0] ];
}
// Calculo de alfa_t[j]
for( t = 1; t < T; t++ )
{
for( j = 0; j < m_nNumEstados; j++ )
{
aux = j*T + t;
m_prAlfa[aux] = 0.0;
for( i = 0; i < m_nNumEstados; i++ )
{
m_prAlfa[aux] += m_prAlfa[i*T + t-1]
* m_prA[i*m_nNumEstados + j];
}
m_prAlfa[aux] *= m_prB[j*m_nNumElementos + m_Sec[t]];
}
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
}
B.6. HMM.CPP 211
void HMM::CalcBeta()
{
int t, i, j, aux;
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
if( m_Sec.size() == 0 )
{
return;
}
// Longitud de la secuencia
int T = (int)m_Sec.size();
// Reserva de memoria para los vectores beta
if( m_prBeta )
delete m_prBeta;
m_prBeta = new real[m_nNumEstados * T];
// Calculo de beta_T(i)
for( i = 0; i < m_nNumEstados; i++ )
{
m_prBeta[i*T + T-1] = 1.0;
}
// Calculo de beta_t(i)
for( t = T-2; t >= 0; t-- )
{
for( i = 0; i < m_nNumEstados; i++ )
{
aux = i*T + t;
m_prBeta[aux] = 0.0;
for( j = 0; j < m_nNumEstados; j++ )
{
m_prBeta[aux] += m_prA[i*m_nNumEstados + j]
* m_prB[j*m_nNumElementos
+ m_Sec[t+1]] * m_prBeta[j*T + t+1];
212 APENDICE B. CODIGO EN C++
}
}
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
}
vector<int> HMM::Viterbi()
{
vector<int> SecEst;
int i, j, t;
if( m_Sec.size() == 0 )
{
return SecEst;
}
int T = (int)m_Sec.size();
SecEst.resize( T );
// Reserva de memoria para los vectores Delta y Psi.
real *Delta = new real[m_nNumEstados * T];
int *Psi = new int[m_nNumEstados * T];
for( i = 0; i < m_nNumEstados; i++ )
{
Delta[i*T] = -log( m_prPi[i] )
-log( m_prB[i*m_nNumElementos + m_Sec[0]] );
Psi[i*T] = -1;
}
real aux, aux2;
int aux_i;
for( j = 0; j < m_nNumEstados; j++ )
{
for( t = 1; t < T; t++ )
{
aux_i = 0;
B.6. HMM.CPP 213
aux = Delta[t-1] - log( m_prA[i*m_nNumEstados + j] );
for( i = 1; i < m_nNumEstados; i++ )
{
aux2 = Delta[i*T + t-1]
- log( m_prA[m_nNumEstados + j] );
if( aux2 < aux )
{
aux = aux2;
aux_i = i;
}
}
Delta[j*T + t] = aux
- log( m_prB[j*m_nNumElementos + m_Sec[t]] );
Psi[j*T + t] = aux_i;
}
}
SecEst[T-1] = 0;
aux = Delta[T-1];
for( i = 1; i < m_nNumEstados; i++ )
{
aux2 = Delta[i*T + T-1];
if( aux > aux2 )
{
SecEst[T-1] = i;
aux = aux2;
}
}
for( t = T-2; t >= 0; t++ )
{
SecEst[t] = Psi[SecEst[t+1]*T + t+1];
}
delete Delta;
delete Psi;
214 APENDICE B. CODIGO EN C++
return SecEst;
}
void HMM::Paso_Baum_Welch()
{
int i, j, k, t;
real P;
if( m_Sec.size() == 0 )
return;
int T = (int)m_Sec.size();
real *Gamma = new real[m_nNumEstados * T];
real *Xi = new real[m_nNumEstados * m_nNumEstados * T];
if( !m_prAlfa )
CalcAlfa();
if( !m_prBeta )
CalcBeta();
P = Probabilidad();
for( i = 0; i < m_nNumEstados; i++ )
{
for( t = 0; t < T; t++ )
{
Gamma[i*T + t] = m_prAlfa[i*T + t] * m_prBeta[i*T + t];
for( j = 0; j < m_nNumEstados; j++ )
{
if( t != T-1 )
Xi[i*(m_nNumEstados*T) + j*T + t] =
m_prAlfa[i*T + t]
* m_prA[i*m_nNumEstados + j]
* m_prB[j*m_nNumElementos + m_Sec[t]]
* m_prBeta[j*T + t+1];
else
Xi[i*(m_nNumEstados*T) + j*T + t] = 0;
B.6. HMM.CPP 215
}
}
}
real *sumGamma = new real[m_nNumEstados];
real sumXi;
real sumGammaCond;
real sumGamma0 = 0.0;
for( i = 0; i < m_nNumEstados; i++ )
{
sumGamma0 += Gamma[i*T];
}
for( i = 0; i < m_nNumEstados; i++ )
{
m_prPi[i] = Gamma[i*T] / sumGamma0;
sumGamma[i] = 0.0;
for( t = 0; t < T-1; t++ )
{
sumGamma[i] += Gamma[i*T + t];
}
for( k = 0; k < m_nNumElementos; k++ )
{
sumGammaCond = 0.0;
for( t = 0; t < T; t++ )
{
if( m_Sec[t] == k )
sumGammaCond += Gamma[i*T + t];
}
m_prB[i*m_nNumElementos + k] = sumGammaCond
/ ( sumGamma[i] + Gamma[i*T + T-1] );
}
}
for( i = 0; i < m_nNumEstados; i++ )
216 APENDICE B. CODIGO EN C++
{
for( j = 0; j < m_nNumEstados; j++ )
{
sumXi = 0.0;
for( t = 0; t < T-1; t++ )
{
sumXi += Xi[i*(m_nNumEstados*T) + j*T + t];
}
m_prA[i*m_nNumEstados + j] = sumXi / sumGamma[i];
}
}
delete m_prAlfa;
m_prAlfa = NULL;
delete m_prBeta;
m_prBeta = NULL;
delete Gamma;
delete Xi;
delete sumGamma;
}
real HMM::Probabilidad()
{
real prob = 0.0;
int i;
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return 0.0;
if( !m_Sec.size() == 0 )
{
int T = (int)m_Sec.size();
if( m_prAlfa != NULL )
{
for( i = 0; i < m_nNumEstados; i++ )
{
prob += m_prAlfa[ i*m_nNumEstados + T-1 ];
B.6. HMM.CPP 217
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return 0.0;
}
else if( m_prBeta != NULL )
{
for( i = 0; i < m_nNumEstados; i++ )
{
prob += m_prPi[i]
* m_prB[ i*m_nNumElementos + m_Sec[0] ]
* m_prBeta[ i*m_nNumEstados ];
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return 0.0;
}
else
{
CalcAlfa();
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return 0.0;
for( i = 0; i < m_nNumEstados; i++ )
{
prob += m_prAlfa[ i*m_nNumEstados + T-1 ];
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return 0.0;
}
}
return prob;
}
void HMM::Observacion( vector<int> &Sec )
{
218 APENDICE B. CODIGO EN C++
m_Sec = Sec;
if( m_prAlfa )
{
delete m_prAlfa;
m_prAlfa = NULL;
}
if( m_prBeta )
{
delete m_prBeta;
m_prBeta = NULL;
}
}
real HMM::Probabilidad( vector<int> &Sec )
{
Observacion( Sec );
return Probabilidad();
}
void HMM::EntrenarGrupo( vector< vector<int> > &Secs )
{
int nSec = (int)Secs.size();
bool entrenando = true;
int i, j, k, s, t, siz;
int max_T = 0;
real gamma_sum, gamma_sum_cond, xi_sum, P, P2, pi_sum;
Inicializar();
// max_T = longitud maxima de las secuencias de entrenamiento.
for( s = 0; s < nSec; s++ )
{
siz = (int)Secs[s].size();
max_T = ( max_T < siz )? siz : max_T;
}
real **Gamma = new real*[nSec];
real **Xi = new real*[nSec];
B.6. HMM.CPP 219
real *pi = new real[m_nNumEstados];
real *A = new real[m_nNumEstados * m_nNumEstados];
real *B = new real[m_nNumEstados * m_nNumElementos];
real *Aden = new real[m_nNumEstados * m_nNumElementos];
real *Bden = new real[m_nNumEstados * m_nNumElementos];
real *tempPi;
real *tempA;
real *tempB;
int numPasos = 0;
while( entrenando )
{
P = 0.0;
memset( pi,0,sizeof(real)*m_nNumEstados );
memset( A,0,sizeof(real)*m_nNumEstados*m_nNumEstados );
memset( B,0,sizeof(real)*m_nNumEstados*m_nNumElementos );
memset( Aden,0,sizeof(real)*m_nNumEstados*m_nNumEstados );
memset( Bden,0,sizeof(real)*m_nNumEstados*m_nNumElementos );
for( s = 0; s < nSec; s++ )
{
Observacion( Secs[s] );
CalcAlfa();
CalcBeta();
P += Probabilidad();
Gamma[s] = new real[m_nNumEstados * max_T];
Xi[s] = new real[m_nNumEstados * m_nNumEstados * max_T];
CalcXi( Xi[s], max_T );
CalcGamma( Gamma[s], max_T, Xi[s] );
}
// Entrenamiento de la matriz A;
for( i = 0; i < m_nNumEstados; i++ )
220 APENDICE B. CODIGO EN C++
{
for( j = 0; j < m_nNumEstados; j++ )
{
gamma_sum = 0.0;
xi_sum = 0.0;
for( s = 0; s < nSec; s++ )
{
siz = (int)Secs[s].size();
//if( siz <= 1 ) continue;
for( t = 0; t < siz-1; t++ )
{
gamma_sum += Gamma[s][max_T * i + t];
xi_sum += Xi[s][(i*m_nNumEstados + j)*max_T + t];
}
}
A[m_nNumEstados * i + j] += xi_sum;
Aden[m_nNumEstados * i + j] += gamma_sum;
}
}
// Entrenamiento de los vectores B y pi
for( i = 0; i < m_nNumEstados; i++ )
{
gamma_sum = 0.0;
for( s = 0; s < nSec; s++ )
{
pi[i] += Gamma[s][max_T*i];
siz = (int)Secs[s].size();
for( t = 0; t < siz; t++ )
{
gamma_sum += Gamma[s][max_T*i + t];
}
}
for( k = 0; k < m_nNumElementos; k++ )
{
gamma_sum_cond = 0.0;
for( s = 0; s < nSec; s++ )
B.6. HMM.CPP 221
{
siz = (int)Secs[s].size();
for( t = 0; t < siz; t++ )
{
if( Secs[s][t] == k )
gamma_sum_cond += Gamma[s][max_T*i + t];
}
}
B[m_nNumElementos*i + k] += gamma_sum_cond;
Bden[m_nNumElementos*i + k] += gamma_sum;
}
}
// Division
k = m_nNumEstados;
pi_sum = (real)0.0;
for( i = 0; i < k; i++ )
{
pi_sum += pi[i];
}
for( i = 0; i < k; i++ )
{
pi[i] /= pi_sum;
}
k = m_nNumEstados * m_nNumEstados;
for( i = 0; i < k; i++ )
{
if( Aden[i] != 0.0 )
A[i] /= Aden[i];
}
k = m_nNumEstados * m_nNumElementos;
for( i = 0; i < k; i++ )
{
if( Bden[i] != 0.0 )
B[i] /= Bden[i];
}
// Comprobacion
tempPi = m_prPi;
tempA = m_prA;
222 APENDICE B. CODIGO EN C++
tempB = m_prB;
m_prPi = pi;
m_prA = A;
m_prB = B;
P2 = 0.0;
for( s = 0; s < nSec; s++ )
{
Observacion( Secs[s] );
real Prob = Probabilidad();
int SecSize = (int)Secs[s].size();
P2 += Prob;
}
if( P2 > P )//|| numPasos < 1000 )
{
pi = tempPi;
A = tempA;
B = tempB;
}
else
entrenando = false;
// Comprobacion de resultado valido (para depuracion).
NotReal( P2 );
numPasos++;
}
delete tempPi;
delete tempA;
delete tempB;
for( s = 0; s < nSec; s++ )
{
delete Gamma[s];
delete Xi[s];
}
delete Gamma;
B.6. HMM.CPP 223
delete Xi;
}
void HMM::CalcGamma( real *gamma, int max_T, real *xi )
{
int i, j, t;
int T = (int)m_Sec.size();
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
if( !xi )
{
xi = new real[m_nNumEstados * max_T];
CalcXi( xi, max_T );
}
else
{
if( !m_prAlfa )
CalcAlfa();
if( !m_prBeta )
CalcBeta();
}
for( t = 0; t < T-1; t++ )
{
for( i = 0; i < m_nNumEstados; i++ )
{
gamma[i*max_T + t] = 0.0;
for( j = 0; j < m_nNumEstados; j++ )
{
gamma[i*max_T + t]
+= xi[(i*m_nNumEstados + j)*max_T + t];
}
}
}
for( i = 0; i < m_nNumEstados; i++ )
{
224 APENDICE B. CODIGO EN C++
gamma[i*max_T + T-1] = m_prBeta[i*T + T-1];
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
}
void HMM::CalcXi( real *xi, int max_T )
{
int i, j, t;
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
// Se calculan las variables de avance y retroceso si no se ha
// hecho ya.
if( !m_prAlfa )
CalcAlfa();
if( !m_prBeta )
CalcBeta();
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
int T = (int)m_Sec.size();
real suma;
int indice;
for( t = 0; t < T-1; t++ )
{
suma = 0.0;
for( i = 0; i < m_nNumEstados; i++ )
{
for( j = 0; j < m_nNumEstados; j++ )
{
indice = (i*m_nNumEstados + j)*max_T + t;
B.6. HMM.CPP 225
xi[indice] = m_prAlfa[i*T + t]
* m_prA[i*m_nNumEstados + j]
* m_prB[j*m_nNumElementos + m_Sec[t+1]]
* m_prBeta[j*T + t+1];
suma += xi[indice];
}
}
for( i = 0; i < m_nNumEstados; i++ )
{
for( j = 0; j < m_nNumEstados; j++ )
{
indice = (i*m_nNumEstados + j)*max_T + t;
if( suma == 0.0 )
xi[indice] = 0.0;
else
xi[indice] /= suma;
}
}
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
}
void HMM::EntrenarSecuencia( vector<int> &Sec )
{
bool entrenando = true;
real P, P2;
Observacion( Sec );
P = Probabilidad();
while( entrenando )
{
printf( "%f\n", P );
Paso_Baum_Welch();
226 APENDICE B. CODIGO EN C++
P2 = Probabilidad();
if( P2 > P )
P = P2;
else
entrenando = false;
}
}
void HMM::Save( string &filename )
{
fstream file;
file.open( filename.c_str(),
ios::out | ios::trunc | ios::binary );
if( !file.is_open() )
return; // ERROR!!!
Save( file );
file.close();
}
void HMM::Save( fstream &file )
{
file.write( (char *)&m_nNumElementos, sizeof(int) );
file.write( (char *)&m_nNumEstados, sizeof(int) );
file.write( (char *)&m_nTipo, sizeof(int) );
file.write( (char *)m_prA,
sizeof(real)*m_nNumEstados*m_nNumEstados );
file.write( (char *)m_prB,
sizeof(real)*m_nNumEstados*m_nNumElementos );
file.write( (char *)m_prPi, sizeof(real)*m_nNumEstados );
}
void HMM::Load( string &filename )
{
fstream file;
file.open( filename.c_str(), ios::in | ios::binary );
B.6. HMM.CPP 227
if( !file.is_open() )
return; // ERROR!!!
Load( file );
file.close();
}
void HMM::Load( fstream &file )
{
int newID;
bool load_matrices;
if( m_prA )
delete m_prA;
if( m_prB )
delete m_prB;
if( m_prPi )
delete m_prPi;
if( m_prAlfa )
delete m_prAlfa;
if( m_prBeta )
delete m_prBeta;
file.read( (char *)&m_nNumElementos, sizeof(int) );
file.read( (char *)&m_nNumEstados, sizeof(int) );
file.read( (char *)&m_nTipo, sizeof(int) );
int longA = m_nNumEstados * m_nNumEstados;
int longB = m_nNumEstados * m_nNumElementos;
m_prA = new real[longA];
m_prB = new real[longB];
m_prPi = new real[m_nNumEstados];
file.read( (char *)m_prA, sizeof(real)*longA );
file.read( (char *)m_prB, sizeof(real)*longB );
file.read( (char *)m_prPi, sizeof(real)*m_nNumEstados );
}
228 APENDICE B. CODIGO EN C++
bool NotReal( real r )
{
string sR;
char cR[256];
sprintf( cR, "%f", r );
sR = cR;
return( sR == "1.#INF00" ||
sR == "-1.#INF00" ||
sR == "1.#IND00" ||
sR == "-1.#IND00" ||
sR == "1.#QNAN00" ||
sR == "-1.#QNAN00" );
}
B.7. SOUNDCAPTURE.H 229
B.7. SoundCapture.h
#ifndef __SOUNDCAPTURE_H__
#define __SOUNDCAPTURE_H__
#include <dsound.h>
#include "Common.h"
#define FREQUENCY 22050
#define BITSPERSAMPLE 16
#define SAMPLE short int
//#define FREQUENCY 11025
//#define BITSPERSAMPLE 8
//#define SAMPLE BYTE
class CSoundCapture
{
//! \brief Instancia unica de la clase.
//!
//! La clase sigue el patron \a singleton. Esto quiere decir
//! que durante la ejecucion de la apliacacion solo existira
//! una instancia de esta clase, \a CSoundCapture::m_pInstance.
static CSoundCapture *m_pInstance;
//! Dispositio de captura de audio de Direct Sound.
IDirectSoundCapture8 *m_pDSC;
//! Buffer del dispositivo de captura de audio.
IDirectSoundCaptureBuffer8 *m_pDSCBuffer;
//! Numero de notificaciones para el \a buffer de captura.
int m_iNotificaciones;
//! \a Handle de notificacion para el \a buffer.
HANDLE *m_phNotificationEvent;
//! Datos de las notificaciones de posicion del \a buffer de
//! lectura.
DSBPOSITIONNOTIFY *m_pDSBPosNotify;
//! Estructura de descripcion de la onda de audio.
WAVEFORMATEX m_wfx;
//! Estructura de descripcion del buffer de captura.
DSCBUFFERDESC m_dscbd;
230 APENDICE B. CODIGO EN C++
//! Variable para controlar el punto de lectura sobre el buffer.
DWORD m_dwNextOffSet;
//! Bloque de memoria donde se almacenan los datos capturados
//! hasta el momento.
BYTE *m_pbStoredData;
//! Longitud (en bytes) del bloque almacenado en
//! \a m_pbStoredData.
unsigned int m_uiStoredDataLength;
//! Constructor.
//!
//! El constructor es privado porque la clase sigue el patron
//! \a singleton. La instancia se crea la primera vez que se
//! llama al metodo GetSoundCapture() y se devuelve la misma
//! instancia en cada llamada posterior al mismo metodo.
CSoundCapture();
//! Destructor.
//!
//! El destructor es privado para seguir el patron
//! \a singleton. La instancia se destruye con el metodo
//! ReleaseSoundCapture().
~CSoundCapture();
//! \brief Inicializacion del sistema de grabacion.
//!
//! @return Ver la documentcion de DirectX para referencias
//! a \a HRESULT.
HRESULT Initialize();
//! Almacena los datos obtenidos del buffer de lectura en el
//! array de analisis
void StoreCapturedData();
public:
//! \brief Devuelve la instancia unica de la clase.
//!
//! Si es la primera vez que se llama al metodo crea la
//! instancia y la devuelve.
static CSoundCapture *GetSoundCapture();
B.7. SOUNDCAPTURE.H 231
//! Destruye la instancia unica de la clase.
static void ReleaseSoundCapture();
//! Comienza la captura de audio
void Record();
//! Captura un bloque de 1000 muestras a traves del dispositivo
//! configurado.
void CaptureBlock();
//! Detiene la captura de audio.
void Stop();
//! Longitud del bloque almacenado.
//!
//! @return La longitud del bloque capturado en bytes.
int GetLength();
//! Obtener el bloque capturado.
//!
//! @return El puntero al array de la se~nal.
real *GetCaptured();
//! Uso provisional.
DWORD m_CapturePosition;
//! Uso provisional.
DWORD m_ReadPosition;
};
#endif //__SOUNDCAPTURE_H__
232 APENDICE B. CODIGO EN C++
B.8. SoundCapture.cpp
#include "SoundCapture.h"
// El puntero a la instancia unica se inicia a NULL.
CSoundCapture *CSoundCapture::m_pInstance = NULL;
CSoundCapture *CSoundCapture::GetSoundCapture()
{
// Si no existe aun una instancia de la clase se crea.
if( !m_pInstance )
m_pInstance = new CSoundCapture;
// Se devuelve la unica instancia de la clase.
return m_pInstance;
}
void CSoundCapture::ReleaseSoundCapture()
{
// Si existe la instancia, se elimina y se inicia el puntero a NULL
if( m_pInstance )
{
delete m_pInstance;
m_pInstance = NULL;
}
}
CSoundCapture::CSoundCapture()
{
// Se ponen los punteros de los datos almacenados a NULL para
// no intentar llamar a ’delete’ sobre un puntero no iniciado.
m_pbStoredData = NULL;
m_uiStoredDataLength = 0;
m_pDSBPosNotify = NULL;
m_pDSC = NULL;
m_pDSCBuffer = NULL;
m_phNotificationEvent = NULL;
HRESULT hr;
B.8. SOUNDCAPTURE.CPP 233
// Construccion y configuracion de los elementos necesarios para
// realizar la captura de audio.
hr = Initialize();
// TODO: Actuar sobre el error.
if( hr != DS_OK )
return;
}
CSoundCapture::~CSoundCapture()
{
m_pInstance = NULL;
// Liberar memoria y recursos
if( m_pbStoredData )
delete m_pbStoredData;
if( m_pDSBPosNotify )
delete m_pDSBPosNotify;
if( m_pDSC )
m_pDSC->Release();
if( m_phNotificationEvent )
delete m_phNotificationEvent;
}
HRESULT CSoundCapture::Initialize()
{
HRESULT hr;
DSCCAPS Caps;
int i;
////////////////////////////////////////////////////////
// Crear dispositivo de captura de sonido de DirectX9 //
////////////////////////////////////////////////////////
// Para comprender el funcionamiento de este metodo lease
// el capıtulo de la ayuda del SDK de DirectX 9.0:
// DirectSound->Using DirectSound->Capturing Waveforms
hr = DirectSoundCaptureCreate8( NULL, &m_pDSC, NULL );
if( hr != DS_OK )
234 APENDICE B. CODIGO EN C++
{
return hr; // ERROR
}
// Comprobacion de la capacidades del dispositivo de captura
// (estructura DSCCAPS).
Caps.dwSize = sizeof( DSCCAPS );
hr = m_pDSC->GetCaps( &Caps );
if( hr != DS_OK )
{
return hr; // ERROR
}
// Estructura de definicion del formato de captura de audio.
m_wfx.cbSize = 0;
m_wfx.nAvgBytesPerSec = FREQUENCY * (BITSPERSAMPLE / 8);
m_wfx.nBlockAlign = BITSPERSAMPLE / 8;
m_wfx.nChannels = 1;
m_wfx.nSamplesPerSec = FREQUENCY;
m_wfx.wBitsPerSample = BITSPERSAMPLE;
m_wfx.wFormatTag = WAVE_FORMAT_PCM;
// Estructura de descripcion del buffer de captura
m_dscbd.dwSize = sizeof(DSCBUFFERDESC);
m_dscbd.dwFlags = 0;
// Buffer de 2 segundos
m_dscbd.dwBufferBytes = m_wfx.nAvgBytesPerSec * 2;
m_dscbd.dwReserved = 0;
m_dscbd.lpwfxFormat = &m_wfx;
m_dscbd.dwFXCount = 0;
m_dscbd.lpDSCFXDesc = NULL;
IDirectSoundCaptureBuffer *pDSCB;
// Creacion del buffer de captura ’pDSCB’
hr = m_pDSC->CreateCaptureBuffer( &m_dscbd , &pDSCB, NULL );
if( hr != DS_OK )
{
return hr; // ERROR
}
// Peticion de la iterfaz del buffer de captura de DirectX 8
B.8. SOUNDCAPTURE.CPP 235
// en m_pDSCBuffer
hr = pDSCB->QueryInterface( IID_IDirectSoundCaptureBuffer8,
(LPVOID*)&m_pDSCBuffer );
if( hr != DS_OK )
{
return hr; // ERROR
}
// Obtencion de la interfaz de notificacion.
IDirectSoundNotify8 *pDSNotify;
hr = pDSCB->QueryInterface( IID_IDirectSoundNotify8,
(LPVOID *)&pDSNotify );
if( hr != DS_OK )
{
return hr; // ERROR
}
// Definicion de las notificaciones que hara el sistema al
// ir recorriendo el buffer de captura. Actualmente no se usan
// en las pruebas pero se mantiene para facilitar su uso en el
// futuro.
m_iNotificaciones = 4;
m_pDSBPosNotify = new DSBPOSITIONNOTIFY[m_iNotificaciones];
m_phNotificationEvent = new HANDLE[m_iNotificaciones];
// Crear eventos
for( i = 0; i < m_iNotificaciones; i++ )
m_phNotificationEvent[i] =
CreateEvent( NULL, TRUE, FALSE, NULL );
// Descripcion de las notificaciones.
for( i = 0; i < m_iNotificaciones; i++ )
{
m_pDSBPosNotify[i].hEventNotify = m_phNotificationEvent[i];
m_pDSBPosNotify[i].dwOffset =
( ( m_wfx.nAvgBytesPerSec / 2 )
* ( i + 1 ) ) - 1 ;
if( m_pDSBPosNotify[i].hEventNotify == NULL )
{
hr = GetLastError();
236 APENDICE B. CODIGO EN C++
return hr; // ERROR
}
}
// Determinacion de las posiciones de notificacion.
pDSNotify->SetNotificationPositions( m_iNotificaciones,
m_pDSBPosNotify );
// Liberar interfaces
pDSNotify->Release();
pDSCB->Release();
// Si ha llegado hasta aquı no ha habido ningun error
return DS_OK;
}
void CSoundCapture::Record()
{
HRESULT hr;
// Punto de lectura al inicio del buffer.
m_dwNextOffSet = 0;
// Comienzo de la grabacion.
hr = m_pDSCBuffer->Start( DSCBSTART_LOOPING );
}
void CSoundCapture::CaptureBlock()
{
HRESULT hr;
static int iLastReadPos = -1;
bool bDone = false;
int next = (m_dwNextOffSet + 4000) % m_dscbd.dwBufferBytes;
while( !bDone )
{
hr = m_pDSCBuffer->GetCurrentPosition( &m_CapturePosition,
&m_ReadPosition );
B.8. SOUNDCAPTURE.CPP 237
if( hr != DS_OK )
{
// Error en GetCurrentPosition
}
// Si el ındice ’next’ esta casi al final del buffer y la
// posicion de lectura ha vuelto ya al principio del mismo
// se corrige next para ver la distancia real entre ellos.
if( ( (int)m_ReadPosition < iLastReadPos ) &&
( next > (int)( m_dscbd.dwBufferBytes /2 ) ) )
{
next -= m_dscbd.dwBufferBytes;
}
// Se guardan los datos capturados cuando la posicion de
// lectura esta pos delante de ’next’ y no esta ‘next‘ al
// principio del buffer y el punto de lectura al final;
// este tiene que dar la vuelta y volver al principio.
if( (int)m_ReadPosition > next &&
!( (int)m_ReadPosition > (2 * m_dscbd.dwBufferBytes / 3)
&& next < m_dscbd.dwBufferBytes / 3 ) )
{
StoreCapturedData();
bDone = true;
}
iLastReadPos = m_ReadPosition;
}
}
void CSoundCapture::Stop()
{
HRESULT hr;
// Almacena los datos obtenidos desde la ultima llamada
// a GetBlock()
hr = m_pDSCBuffer->Stop();
}
void CSoundCapture::StoreCapturedData()
238 APENDICE B. CODIGO EN C++
{
HRESULT hr;
//DWORD capturePosition, readPosition;
void *pCapturedData1, *pCapturedData2;
DWORD capturedDataLength1, capturedDataLength2;
DWORD blockLength;
BYTE *pbStoredDataAux;
hr = m_pDSCBuffer->Lock( m_dwNextOffSet, 4000, &pCapturedData1,
&capturedDataLength1, &pCapturedData2,
&capturedDataLength2, 0 );
if( hr != DS_OK )
{
//cout << "Error en Lock" << endl;
}
pbStoredDataAux = m_pbStoredData;
// Longitud del nuevo bloque.
blockLength = capturedDataLength1 + capturedDataLength2;
m_pbStoredData = new BYTE[m_uiStoredDataLength + blockLength];
// Recolocar la marca de offset para la proxima captura.
m_dwNextOffSet += blockLength;
m_dwNextOffSet %= m_dscbd.dwBufferBytes;
if( pbStoredDataAux )
{
memcpy( m_pbStoredData, pbStoredDataAux,
m_uiStoredDataLength );
delete pbStoredDataAux;
}
if( pCapturedData1 )
memcpy( m_pbStoredData + m_uiStoredDataLength,
pCapturedData1, capturedDataLength1 );
if( pCapturedData2 )
memcpy( m_pbStoredData + m_uiStoredDataLength +
B.8. SOUNDCAPTURE.CPP 239
capturedDataLength1, pCapturedData2,
capturedDataLength2 );
hr = m_pDSCBuffer->Unlock( pCapturedData1, capturedDataLength1,
pCapturedData2, capturedDataLength2 );
if( hr != DS_OK )
{
//cout << "Error en Unlock" << endl;
}
m_uiStoredDataLength += blockLength;
}
int CSoundCapture::GetLength()
{
return m_uiStoredDataLength / ( BITSPERSAMPLE / 8 );
}
real *CSoundCapture::GetCaptured()
{
int iLength = m_uiStoredDataLength / ( BITSPERSAMPLE / 8 );
SAMPLE *pCap = (SAMPLE *)m_pbStoredData;
real *prCap = new real[iLength];
for( int i = 0; i < iLength; i++ )
{
prCap[i] = pCap[i];
}
return prCap;
}
240 APENDICE B. CODIGO EN C++
B.9. SoundCaptureLink.h
#ifndef __SOUNDCAPTURELINK_H__
#define __SOUNDCAPTURELINK_H__
#include "..\Application\Common.h"
class CSoundCapture;
class CSoundCaptureLink
{
CSoundCaptureLink();
~CSoundCaptureLink();
//! Instancia de la clase \a CSoundCapture a la que enlaza.
static CSoundCapture *m_pSoundCapture;
//! Instancia unica de esta clase.
static CSoundCaptureLink *m_pInstance;
public:
//! \brief Devuelve la instancia unica de la clase.
//!
//! Si es la primera vez que se llama al metodo crea la
//! instancia y la devuelve.
static CSoundCaptureLink *GetSoundCaptureLink();
//! Destruye la instancia unica de la clase.
static void ReleaseSoundCaptureLink();
//! Comienza la captura de audio
void Record();
//! Captura un bloque de 1000 muestras a traves del dispositivo
//! configurado.
void CaptureBlock();
//! Detiene la captura de audio.
void Stop();
//! Longitud del bloque almacenado.
//!
//! @return La longitud del bloque capturado en bytes.
B.9. SOUNDCAPTURELINK.H 241
int GetLength();
//! Obtener el bloque capturado.
//!
//! @return El puntero al array de la se~nal.
real *GetCaptured();
};
#endif //__SOUNDCAPTURELINK_H__
242 APENDICE B. CODIGO EN C++
B.10. SoundCaptureLink.cpp
#include "SoundCaptureLink.h"
#include "..\Application\SoundCapture.h"
CSoundCaptureLink *CSoundCaptureLink::m_pInstance = NULL;
CSoundCapture *CSoundCaptureLink::m_pSoundCapture = NULL;
CSoundCaptureLink::CSoundCaptureLink()
{
m_pSoundCapture = CSoundCapture::GetSoundCapture();
}
CSoundCaptureLink::~CSoundCaptureLink()
{
}
CSoundCaptureLink *CSoundCaptureLink::GetSoundCaptureLink()
{
if( !m_pInstance )
m_pInstance = new CSoundCaptureLink();
return m_pInstance;
}
void CSoundCaptureLink::ReleaseSoundCaptureLink()
{
CSoundCapture::ReleaseSoundCapture();
m_pSoundCapture = NULL;
delete m_pInstance;
m_pInstance = NULL;
}
void CSoundCaptureLink::CaptureBlock()
{
m_pSoundCapture->CaptureBlock();
}
real *CSoundCaptureLink::GetCaptured()
B.10. SOUNDCAPTURELINK.CPP 243
{
return m_pSoundCapture->GetCaptured();
}
int CSoundCaptureLink::GetLength()
{
return m_pSoundCapture->GetLength();
}
void CSoundCaptureLink::Record()
{
m_pSoundCapture->Record();
}
void CSoundCaptureLink::Stop()
{
m_pSoundCapture->Stop();
}
244 APENDICE B. CODIGO EN C++
B.11. PhonRec.h
#ifndef __PHONREC_H__
#define __PHONREC_H__
#include "Common.h"
#include "HMM.h"
using namespace std;
//! \brief Fonema.
//!
//! Esta clase representa un fonema de un idioma concreto en el
//! sistema de reconocimiento de voz. El fonema se caracteriza por
//! las secuencias correspondientes al analisis de la se~nal de audio
//! de las distintas pronunciaciones introducidas. Tras el
//! entrenamiento correspondiente estas caracterısticas se plasman
//! en el Modelo Oculto de Markov (HMM) asociado.
class CPhonRec
{
//! Modelo Oculto de Markov asociado a la pronunciacion.
HMM m_HMM;
//! Nombre del fonema.
string m_sPhon;
//! Etiqueta para el elemento.
string m_sLabel;
//! Valor medio del logaritmo de la probabilidad para el modelo
//! de este fonema.
real m_rProbAverage;
//! Secuencias correspondientes a las distintas pronunciaciones
//! asociadas al fonema.
vector< vector<int> > m_vviUtterances;
public:
//! \brief Constructor.
//!
//! @param sPhon Nombre del fonema.
B.11. PHONREC.H 245
//! @param sLabel Etiqueta del fonema.
CPhonRec( string &sPhon, string &sLabel );
//! \brief Constructor.
//!
//! @param filename Nombre del archivo.
CPhonRec( string &filename );
//! \brief Constructor.
//!
//! @param file Origen de datos del archivo desde el que se
//! quiere cargar el elemento.
CPhonRec( fstream &file );
//! Destructor.
~CPhonRec();
//! \brief Obtener el nombre del fonema.
//!
//! @return Instancia \a string con el nombre del fonema.
string GetPhonName();
//! \brief Obtener la etiqueta del fonema.
//!
//! @return Instancia \a string con la etiqueta del fonema.
string GetPhonLabel();
//! \brief A~nadir pronunciacion.
//!
//! A~nade una secuencia de observacion referente a una
//! pronunciacion del fonema en estudio.
//! @param vUtterance Secuencia de observacion.
void AddUtterance( vector<int> &vUtterance );
//! \brief Evaluar pronunciacion.
//!
//! Evalua la probabilidad de que la secuencia dada corresponda
//! con el fonema que representa esta instancia.
//! @param vUtterance Secuencia de observacion.
//! @return Logaritmo de la probabilidad de ocurrencia de la
//! secuencia de entrada.
real LogEvaluateUtterance( vector<int> &vUtterance );
//! \brief Evaluar pronunciacion.
//!
//! Evalua la probabilidad de que la secuencia dada corresponda
246 APENDICE B. CODIGO EN C++
//! con el fonema que representa esta instancia.
//! @param vUtterance Secuencia de observacion.
//! @return Probabilidad de ocurrencia de la secuencia de
//! entrada.
real EvaluateUtterance( vector<int> &vUtterance );
//! \brief Entrenamiento del modelo de markov del elemento.
void TrainUtterances();
void SetAverage( real rAverage );
//! \brief Guarda el termino en el archivo especificado.
//!
//! @param filename Nombre del archivo con la ruta completa.
void Save( string &filename );
//! \brief Guarda elemento y sus datos en el archivo
//! especificado.
//!
//! @param file Origen de datos del archivo.
void Save( fstream &file );
//! \brief Carga el fonema desde el archivo especificado.
//!
//! @param filename Nombre del archivo con la ruta completa.
void Load( string &filename );
//! \brief Carga el fonema y sus datos desde el archivo
//! especificado.
//!
//! @param file Origen de datos del archivo.
void Load( fstream &file );
};
#endif //__PHONREC_H__
B.12. PHONREC.CPP 247
B.12. PhonRec.cpp
#include "PhonRec.h"
CPhonRec::CPhonRec( string &sPhon, string &sLabel )
{
m_sPhon = sPhon;
m_sLabel = sLabel;
m_rProbAverage = 0.0f;
}
CPhonRec::CPhonRec( string &filename )
{
Load( filename );
m_rProbAverage = 0.0f;
}
CPhonRec::CPhonRec( fstream &file )
{
Load( file );
m_rProbAverage = 0.0f;
}
CPhonRec::~CPhonRec()
{
}
string CPhonRec::GetPhonName()
{
return m_sPhon;
}
string CPhonRec::GetPhonLabel()
{
return m_sLabel;
}
void CPhonRec::AddUtterance( vector<int> &vUtterance )
{
m_vviUtterances.push_back( vUtterance );
}
248 APENDICE B. CODIGO EN C++
real CPhonRec::LogEvaluateUtterance( vector<int> &vUtterance )
{
real prob = m_HMM.Probabilidad( vUtterance );
if( prob != 0 )
{
prob = log10(prob);
prob = (prob-m_rProbAverage);
}
else
prob = -100000000;
return prob;
}
real CPhonRec::EvaluateUtterance( vector<int> &vUtterance )
{
return m_HMM.Probabilidad( vUtterance );
}
void CPhonRec::TrainUtterances()
{
m_HMM.EntrenarGrupo( m_vviUtterances );
}
void CPhonRec::SetAverage( real rAverage )
{
m_rProbAverage = rAverage;
}
void CPhonRec::Save( string &sFilename )
{
fstream file;
file.open( sFilename.c_str(),
ios::binary | ios::trunc | ios::out );
if( !file.is_open() )
return; // ERROR!
Save( file );
B.12. PHONREC.CPP 249
file.close();
}
void CPhonRec::Save( fstream &file )
{
SaveString( m_sPhon, file );
SaveString( m_sLabel, file );
int n = (int)m_vviUtterances.size();
file.write( (char *)&n, sizeof(int) );
for( int i = 0; i < n; i++ )
{
SaveIVector( m_vviUtterances[i], file );
}
m_HMM.Save( file );
}
void CPhonRec::Load( string &sFilename )
{
fstream file;
file.open( sFilename.c_str(), ios::binary | ios::in );
if( !file.is_open() )
return; // ERROR!!
Load( file );
file.close();
}
void CPhonRec::Load( fstream &file )
{
int n, i;
vector<int> v;
LoadString( m_sPhon, file );
LoadString( m_sLabel, file );
file.read( (char *)&n, sizeof(int) );
250 APENDICE B. CODIGO EN C++
for( i = 0; i < n; i++ )
{
LoadIVector( v, file );
m_vviUtterances.push_back( v );
}
m_HMM.Load( file );
}
B.13. WORDREC.H 251
B.13. WordRec.h
#ifndef __WORDREC_H__
#define __WORDREC_H__
#include "Common.h"
#include "LangRec.h"
using namespace std;
class CWordRec
{
//! Palabra.
string m_sWord;
//! Lista de fonemas que forman la pronunciacion de la palabra.
vector<CPhonRec *> m_vPhons;
//! Grupo de fonemas del espa~nol.
CLangRec *m_pLangRec;
real *m_prProbability;
int *m_piIndex;
//! \brief Trascripcion fonetica de la palabra en espa~nol.
//!
//! Realiza la trascirpcion fonetica de la palabra segun las
//! reglas del espa~nol. Crea ası la lista de fonemas (CPhonRec)
//! correspondientes a la pronunciacion de aquella.
void PhoneticTrascription();
//! \brief Construye la matriz de probabilidades acumuladas.
//!
//! Este metodo privado se apoya en el algoritmo de Viterbi para
//! obtener la probabilidad de que una palabra esta pronunciada
//! en una secuencia. La matriz posee la probabilidad de que un
//! determinado fonema se termine de pronunciar en un
//! determinado \a frame. Con la distribucion de los anteriores
//! que hace maxima esta distribucion. En otra matriz se
//! almacena para cada paso la distribucion de dichos fonemas.
void ConstructProbMatrix( vector<int> &vUtterance );
252 APENDICE B. CODIGO EN C++
//! \brief Probabilidad de ocurrencia de una secuencia.
//!
//! Este metodo devuelve la probabilidad de ocurrencia de una
//! subsecuencia para un fonema concreto de la palabra que se
//! esta estudiando.
//! @param fon Indice del fonema en la palabra.
//! @param i Primer elemento de la subsecuencia dentro de la
//! secuencia \a vUtterance.
//! @param f Ultimo elemento de la subsecuencia dentro de la
//! secuencia \a vUtterance.
//! @param vUtterance Secuncia de la que se extrae la
//! subsecuencia.
//! @return El logaritmo de la probabilidad de ocurrencia de la
//! subsecuencia.
real LogProbability( int fon, int i, int f,
vector<int> &vUtterance );
public:
//! \brief Constructor.
//!
//! @param sWord Nombre del elemento.
//! @param pLangRec Puntero a la intsnacia de la clase de
//! definicion de idioma.
CWordRec( string &sWord, CLangRec *pLangRec );
//! Destructor.
~CWordRec();
//! Devuelve el elemento en una instancia \a string
string GetWord();
//! \brief Evaluar pronunciacion.
//!
//! Evalua la probabilidad de que la secuencia dada corresponda
//! con la palabra que representa esta instancia.
//! @param vUtterance Secuencia de observacion.
real EvaluateUtterance( vector<int> &vUtterance );
};
#endif //__WORDREC_H__
B.14. WORDREC.CPP 253
B.14. WordRec.cpp
#include "WordRec.h"
CWordRec::CWordRec( string &sWord, CLangRec *pLangRec )
{
m_pLangRec = pLangRec;
m_sWord = sWord;
m_prProbability = NULL;
m_piIndex = NULL;
PhoneticTrascription();
}
CWordRec::~CWordRec()
{
if( m_prProbability )
delete m_prProbability;
if( m_piIndex )
delete m_piIndex;
}
string CWordRec::GetWord()
{
return m_sWord;
}
real CWordRec::EvaluateUtterance( vector<int> &vUtterance )
{
int nFrames, nPhon;
string filename;
fstream file;
nPhon = m_vPhons.size();
nFrames = vUtterance.size();
if( nPhon == 1 )
{
real prob = LogProbability( 0, 1, nFrames-1, vUtterance );
254 APENDICE B. CODIGO EN C++
return prob;
}
ConstructProbMatrix( vUtterance );
if( !m_prProbability )
{
return -100000000;
}
int i = m_piIndex[ (nPhon * nFrames) - 1 ];
int j = nFrames-1;
real prob = LogProbability( nPhon-1, i, j, vUtterance );
real rProb = m_prProbability[ (nPhon * nFrames) - 1 ];
for( int n = nPhon - 2; n >= 0; n-- )
{
j = i-1;
i = m_piIndex[n*nFrames+j];
prob = LogProbability( n, i, j, vUtterance );
}
return rProb;// / nPhon;
}
void CWordRec::PhoneticTrascription()
{
char *pcWord;
int siz = (int)m_sWord.size();
int i;
pcWord = new char[siz+1];
memcpy( pcWord, m_sWord.c_str(), siz+1 );
strlwr( pcWord );
for( i = 0; i < siz; i++ )
{
if( pcWord[i] == ’~N’ )
pcWord[i] = ’~n’;
}
B.14. WORDREC.CPP 255
//m_vPhons.push_back( m_pLangRec->GetPhonByLabel(".pau") );
for( i = 0; i < siz; i++ )
{
char s = pcWord[i+1];
char t = pcWord[i+2];
switch( pcWord[i] )
{
case ’a’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("a") );
break;
case ’e’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("e") );
break;
case ’i’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("i") );
break;
case ’o’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("o") );
break;
case ’u’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("u") );
break;
case ’b’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("b") );
break;
case ’c’:
if( s == ’h’ )
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("ts") );
else if( s == ’e’ || s == ’i’ )
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("z") );
else
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("k") );
break;
case ’d’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("d") );
break;
case ’f’:
256 APENDICE B. CODIGO EN C++
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("f") );
break;
case ’g’:
if( s == ’e’ || s == ’i’ )
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("x") );
else
{
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("g") );
if( s == ’u’ && ( t == ’e’ || t == ’i’ ) )
i++;
}
break;
case ’h’:
break;
case ’j’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("x") );
break;
case ’k’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("k") );
break;
case ’l’:
if( s == ’l’ )
{
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("j") );
i++;
}
else
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("l") );
break;
case ’m’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("m") );
break;
case ’n’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("n") );
break;
case ’~n’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("ny") );
B.14. WORDREC.CPP 257
break;
case ’p’:
if( !(s == ’s’ && i == 0) )
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("p") );
break;
case ’q’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("k") );
if( s == ’u’ )
i++;
break;
case ’r’:
if( s == ’r’ || i == 0 )
{
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("rr") );
if( s == ’r’ )
i++;
}
else
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("r") );
break;
case ’s’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("s") );
break;
case ’t’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("t") );
break;
case’v’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("b") );
break;
case ’w’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("u") );
break;
case ’x’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("k") );
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("s") );
break;
case ’y’:
if( s == ’a’ || s == ’e’ || s == ’i’
258 APENDICE B. CODIGO EN C++
|| s == ’o’ || s == ’u’ )
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("j") );
else
m_vPhons.push_back(
m_pLangRec->GetPhonByLabel("i") );
break;
case ’z’:
m_vPhons.push_back( m_pLangRec->GetPhonByLabel("z") );
}
}
//m_vPhons.push_back( m_pLangRec->GetPhonByLabel(".pau") );
delete pcWord;
}
void CWordRec::ConstructProbMatrix( vector<int> &vUtterance )
{
int nPhons = (int)m_vPhons.size();
int nFrames = (int)vUtterance.size();
// Arrays para el tama~no maximo o mınimo de cada fonema
int *minLen = new int[nPhons];
int *maxLen = new int[nPhons];
int *sumMin = new int[nPhons];
int *sumMax = new int[nPhons];
int i, j, k, i1, i2;
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
// Se dan los valores a las longitudes maximas y mınimas
// de cada fonema.
for( i = 0; i < nPhons; i++ )
{
if( m_vPhons[i] == NULL )
{
maxLen[i] = 20;
minLen[i] = 0;
B.14. WORDREC.CPP 259
}
else
{
maxLen[i] = 16;
minLen[i] = 3;
}
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
// Se calculan las posiciones maxima y mınima de cada fonema en
// funcion de las longitudes maximas y mınimas.
sumMin[0] = minLen[0];
sumMax[0] = maxLen[0];
for( i = 1; i < nPhons; i++ )
{
sumMin[i] = sumMin[i-1] + minLen[i];
sumMax[i] = sumMax[i-1] + maxLen[i];
}
if( m_prProbability )
{
delete m_prProbability;
m_prProbability = NULL;
}
if( nFrames > sumMax[nPhons-1] )
return;
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
// Inicializamos las matrices que indican la Distorsion
m_prProbability = new real[ nPhons * nFrames ];
if( m_piIndex )
delete m_piIndex;
260 APENDICE B. CODIGO EN C++
m_piIndex = new int[ nPhons * nFrames ];
float mf, aux;
int mi;
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
int j1, j2;
// Para cada segmento excepto el ultimo
for( i = 0; i < nPhons-1; i++ )
{
j1 = sumMin[i];
j2 = min( sumMax[i], nFrames-1 );
// Para cada punto posible de fin del segmento
for( j = j1; j <= j2; j++ )
{
if( i == 0 )
i1 = 1;
else
i1 = max( j-maxLen[i]+1, sumMin[i-1]+1 );
if( i == 0 )
i2 = 1;
else
i2 = min( j-minLen[i]+1, sumMax[i-1]+1 );
mf = -100000000;
mi = i1;
// Entre todos los puntos de inicio posible
for( k = i1; k <= i2; k++ )
{
aux = LogProbability( i, k, j, vUtterance );
if( i != 0 )
aux += m_prProbability[(i-1)*nFrames + k-1];
B.14. WORDREC.CPP 261
// Tomamos aquel de probabilidad acumulada mayor.
if( aux > mf )
{
mf = aux;
mi = k;
}
}
m_prProbability[i*nFrames + j] = mf;
m_piIndex[i*nFrames + j] = mi;
}
}
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
// Ultimo Segmento
mf = -100000000;
i1 = max( nFrames - maxLen[nPhons-1], sumMin[nPhons-2] + 1 );
i2 = min( nFrames - minLen[nPhons-1], sumMax[nPhons-2] + 1 );
mi = i1;
for( k = i1; k <= i2; k++ )
{
aux = LogProbability( nPhons-1, k, nFrames-1, vUtterance );
aux += m_prProbability[(nPhons-2)*nFrames + k-1];
// Tomamos aquel de probabilidad acumulada mayor.
if( aux > mf )
{
mf = aux;
mi = k;
}
}
m_prProbability[(nPhons*nFrames) - 1] = mf;
m_piIndex[(nPhons*nFrames) - 1] = mi;
262 APENDICE B. CODIGO EN C++
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return;
delete minLen;
delete maxLen;
delete sumMax;
delete sumMin;
}
real CWordRec::LogProbability( int fon, int i, int f,
vector<int> &vUtterance )
{
vector<int> subUtterance;
real probSum = 0.0;
real prob = 0.0;
real relprob = 0.0;
int numPhons = m_pLangRec->GetNumPhons();
int numFrames = (int)vUtterance.size();
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return 0.0;
if( fon < 0 || fon >= numPhons || i < 0 || i >= numFrames
|| f < 0 || f >= numFrames || f <= i )
return 0.0;
subUtterance.insert( subUtterance.begin(), vUtterance.begin()+i,
vUtterance.begin()+f+1 );
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return 0.0;
prob = m_vPhons[fon]->LogEvaluateUtterance( subUtterance );
// Control de lagunas de memoria en modo debug.
if( !_CrtCheckMemory() )
return 0.0;
B.14. WORDREC.CPP 263
return prob;
}
264 APENDICE B. CODIGO EN C++
B.15. LangRec.h
#ifndef __LANGREC_H__
#define __LANGREC_H__
#include "Common.h"
#include "PhonRec.h"
//! \brief Grupo de fonemas pertenecientes a un idioma.
//!
//! Esta clase almacena un conjunto de fonemas correspondientes
//! a un idioma para el sistema de reconocimiento de voz.
//! Cada elemento (clase CPhonRec) esta represenado por un nombre,
//! una etiqueta y lo mas importante su Modelo Oculto de Markov
//! caracterıstico (clase HMM).
class CLangRec
{
//! Nombre del idioma.
string m_sName;
//! Vector de fonemas incluidos.
vector<CPhonRec *> m_vpPhons;
public:
//! \brief Constructor vacio
CLangRec();
//! \brief Constructor cargando el contenido desde disco.
//! @param filename Nombre del archivo con la ruta completa.
CLangRec( const string &filename );
//! \brief Constructor cargando el contenido desde disco.
//! @param file Origen de datos del archivo desde el que se
//! quiere cargar.
CLangRec( fstream &file );
//! \brief Destructor
~CLangRec();
//! \brief A~nade un fonema a la configuracion de un idioma.
//!
//! @param sPhon Nombre del fonema.
//! @param sLabel Etiqueta del fonema.
//! @return El puntero a la instancia de la clase CPhonRec que
B.15. LANGREC.H 265
//! se ha a~nadido.
CPhonRec *AddPhon( string &sPhon, string &sLabel );
//! \brief A~nade un elemento nuevo cargando sus datos desde
//! disco.
//!
//! @param filename Nombre del archivo del que se quiere cargar
//! el fonema.
//! @return El puntero a la instancia de la clase CPhonRec que
//! se ha a~nadido.
CPhonRec *AddPhon( string &filename );
//! \brief A~nade un elemento nuevo cargando sus datos desde
//! disco.
//!
//! @param file Origen de datos del archivo del que se quiere
//! cargar el fonema.
//! @return El puntero a la instancia de la clase CPhonRec que
//! se ha a~nadido.
CPhonRec *AddPhon( fstream &file );
//! \brief Obtener el numero de fonemas del idioma.
//!
//! @return Entero con el numero de elementos.
int GetNumPhons();
//! \brief Obtener fonema.
//!
//! @param index Indice del elemento en la lista de fonemas.
//! @return Puntero a la instancia CPhonRec del elemento de
//! ındice \a index. Devuelve NULL si el ındice esta fuera de
//! rango.
CPhonRec *GetPhon( int index );
//! \brief Obtener fonema.
//!
//! @param sLabel Etiqueta del fonema que se quiere obtener.
//! @return Puntero a la instancia CPhonRec del elemento de
//! etiqueta \a sLabel. Devuelve \a NULL si no esta contenido
//! en el grupo de fonemas de este idioma.
CPhonRec *GetPhonByLabel( const string &sLabel );
//! \brief Guarda el diccionario en el archivo especificado.
266 APENDICE B. CODIGO EN C++
//!
//! @param filename Nombre del archivo con la ruta completa.
void Save( const string &filename );
//! \brief Guarda el diccionario en el archivo especificado.
//!
//! @param file Origen de datos del archivo.
void Save( fstream &file );
//! \brief Carga el diccionario desde el archivo especificado.
//!
//! @param filename Nombre del archivo con la ruta completa.
void Load( const string &filename );
//! \brief Carga el diccionario desde el archivo especificado.
//!
//! @param file Origen de datos del archivo.
void Load( fstream &file );
};
#endif //__LANGREC_H__
B.16. LANGREC.CPP 267
B.16. LangRec.cpp
#include "LangRec.h"
CLangRec::CLangRec()
{
m_sName = "Espa~nol";
}
CLangRec::CLangRec( fstream &file )
{
Load( file );
}
CLangRec::CLangRec( const string &filename )
{
Load( filename );
}
CLangRec::~CLangRec()
{
for( int i = 0; i < (int)m_vpPhons.size(); i++ )
{
delete m_vpPhons[i];
}
m_vpPhons.clear();
}
CPhonRec *CLangRec::AddPhon( string &filename )
{
CPhonRec *pPhon = new CPhonRec( filename );
m_vpPhons.push_back( pPhon );
return pPhon;
}
CPhonRec *CLangRec::AddPhon( fstream &file )
{
CPhonRec *pPhon = new CPhonRec( file );
268 APENDICE B. CODIGO EN C++
m_vpPhons.push_back( pPhon );
return pPhon;
}
CPhonRec *CLangRec::AddPhon( string &sPhon, string &sLabel )
{
CPhonRec *pPhon = new CPhonRec( sPhon, sLabel );
m_vpPhons.push_back( pPhon );
return pPhon;
}
int CLangRec::GetNumPhons()
{
return (int)m_vpPhons.size();
}
CPhonRec *CLangRec::GetPhon( int index )
{
if( index < 0 || index >= (int)m_vpPhons.size() )
return NULL;
else
return m_vpPhons[index];
}
CPhonRec *CLangRec::GetPhonByLabel( const string &sLabel )
{
for( int i = 0; i < (int)m_vpPhons.size(); i++ )
{
if( m_vpPhons[i]->GetPhonLabel() == sLabel )
return m_vpPhons[i];
}
return NULL;
}
void CLangRec::Save( const string &filename )
{
B.16. LANGREC.CPP 269
fstream file;
file.open( filename.c_str(),
ios::binary | ios::trunc | ios::out );
if( !file.is_open() )
return; // ERROR!
Save( file );
file.close();
}
void CLangRec::Save( fstream &file )
{
int iSize = (int)m_vpPhons.size();
SaveString( m_sName, file );
file.write( (char *)&iSize, sizeof(int) );
for( int i = 0; i < iSize; i++ )
{
m_vpPhons[i]->Save( file );
}
}
void CLangRec::Load( const string &filename )
{
fstream file;
file.open( filename.c_str(), ios::binary | ios::in );
if( !file.is_open() )
return; // ERROR!!
Load( file );
file.close();
}
void CLangRec::Load( fstream &file )
{
int iSize, i;
270 APENDICE B. CODIGO EN C++
LoadString( m_sName, file );
file.read( (char *)&iSize, sizeof(int) );
for( i = 0; i < iSize; i++ )
{
AddPhon( file );
}
}
B.17. DICTREC.H 271
B.17. DictRec.h
#ifndef __DICTREC_H__
#define __DICTREC_H__
#include "Common.h"
#include "LangRec.h"
#include "WordRec.h"
//! \brief Diccionario de terminos identificables.
//!
//! Esta clase almacena un conjunto de palabras identificables
//! mediante el sistema de reconocimiento de voz.
class CDictRec
{
//! Nombre del diccionario.
string m_sName;
//! Nombre del archivo del que se cargan los fonemas del idioma
//! empleado.
string m_sLangRecFile;
//! Grupo de fonemas del idioma empleado.
CLangRec *m_pLangRec;
//! Vector de elementos identificables.
vector<CWordRec *> m_vpWords;
public:
//! \brief Constructor dando la instancia de la clase de
//! configuracion del idioma.
//! @param name Nombre que se quiere dar al diccionario.
//! @param pLangRec Puntero a la instancia de la clase de
//! configuracion del idioma.
CDictRec( const string &name, CLangRec *pLangRec );
//! \brief Constructor indicando la coniguracion del idioma.
//! @param name Nombre que se quiere dar al diccionario.
//! @param sLangRecFile Puntero a la clase de configuracion del
//! idioma.
CDictRec( const string &name, const string &sLangRecFile );
//! \brief Constructor cargando el contenido desde disco.
272 APENDICE B. CODIGO EN C++
//! @param filename Nombre del archivo con la ruta completa.
CDictRec( const string &filename );
//! \brief Constructor cargando el contenido desde disco.
//! @param file Origen de datos del archivo desde el que se
//! quiere cargar el diccionario.
CDictRec( fstream &file );
//! \brief Destructor
~CDictRec();
//! \brief A~nade una palabra nueva al diccionario de terminos
//! identificables.
//!
//! @param sWord Palabra a a~nadir.
//! @return El puntero a la instancia de la clase CWordRec que
//! se ha a~nadido.
CWordRec *AddWord( string &sWord );
//! \brief Obtener el numero de terminos en el diccionario.
//!
//! @return Entero con el numero de elementos en el diccionario.
int GetNumWords();
//! \brief Obtener una palabra del diccionario.
//!
//! @param index Indice del elemento en la lista de terminos.
//! @return El puntero a la instancia de la clase CWordRec de
//! ındice \a index.
CWordRec *GetWord( int index );
//! \brief Obtener una palabra del diccionario
//!
//! @param sWord Palabra que se quiere encontrar.
//! @return Puntero a la instancia CWordRec de la palabra
//! \a sWord.
CWordRec *GetWord( const string &sWord );
//! \brief Identificacion de un termino.
//!
//! Identifica a que elemento del diccionario pertenece (con
//! mayor probabilidad) la secuencia de observacion dada.
//! @param vSecuence Secuencia del elemento a identificar.
//! Esta secuencia se obtiene del analisis de la se~nal de voz
//! (clase CSignal) y posterior clasificacion de los
B.17. DICTREC.H 273
//! vectores de coeficientes cepstrales obenidos (clase
//! CFuzzyCMeans).
//! @return Puntero a la instancia CWordRec de la palabra que
//! se estima se ha identificado en la secuencia.
CWordRec *Identify( vector<int> &vSecuence );
//! \brief Guarda el diccionario en el archivo especificado.
//!
//! @param filename Nombre del archivo con la ruta completa.
void Save( const string &filename );
//! \brief Guarda el diccionario en el archivo especificado.
//!
//! @param file Origen de datos del archivo.
void Save( fstream &file );
//! \brief Carga el diccionario desde el archivo especificado.
//!
//! @param filename Nombre del archivo con la ruta completa.
void Load( const string &filename );
//! \brief Carga el diccionario desde el archivo especificado.
//!
//! @param file Origen de datos del archivo.
void Load( fstream &file );
};
#endif //__DICTREC_H__
274 APENDICE B. CODIGO EN C++
B.18. DictRec.cpp
#include "DictRec.h"
CDictRec::CDictRec( const string &name, CLangRec *pLangRec )
{
m_sName = name;
m_sLangRecFile = "";
m_pLangRec = pLangRec;
}
CDictRec::CDictRec( const string &name, const string &sLangRecFile )
{
m_sName = name;
m_sLangRecFile = sLangRecFile;
m_pLangRec = new CLangRec( sLangRecFile );
}
CDictRec::CDictRec( const string &filename )
{
Load( filename );
}
CDictRec::CDictRec( fstream &file )
{
Load( file );
}
CDictRec::~CDictRec()
{
for( int i = 0; i < (int)m_vpWords.size(); i++ )
{
delete m_vpWords[i];
}
if( m_sLangRecFile != "" )
delete m_pLangRec;
}
CWordRec *CDictRec::AddWord( string &sWord )
{
B.18. DICTREC.CPP 275
CWordRec *pWord;
pWord = new CWordRec( sWord, m_pLangRec );
m_vpWords.push_back( pWord );
return pWord;
}
int CDictRec::GetNumWords()
{
return (int)m_vpWords.size();
}
CWordRec *CDictRec::GetWord( int index )
{
if( index >= 0 && index < (int)m_vpWords.size() )
return m_vpWords[index];
else
return NULL;
}
CWordRec *CDictRec::GetWord( const string &sWord )
{
for( int i = 0; i < (int)m_vpWords.size(); i++ )
{
if( m_vpWords[i]->GetWord() == sWord )
return m_vpWords[i];
}
return NULL;
}
CWordRec *CDictRec::Identify( vector<int> &vSecuence )
{
real maxProb = -1.0;
real prob;
int i, maxI = -1;
for( i = 0; i < (int)m_vpWords.size(); i++ )
{
prob = m_vpWords[i]->EvaluateUtterance( vSecuence );
276 APENDICE B. CODIGO EN C++
if( prob>maxProb )
{
maxProb = prob;
maxI = i;
}
}
return m_vpWords[maxI];
}
void CDictRec::Save( const string &filename )
{
fstream file;
file.open( filename.c_str(),
ios::binary | ios::trunc | ios::out );
if( !file.is_open() )
return; // ERROR!
Save( file );
file.close();
}
void CDictRec::Save( fstream &file )
{
int numWords, i;
SaveString( m_sName, file );
SaveString( m_sLangRecFile, file );
numWords = (int)m_vpWords.size();
file.write( (char *)&numWords, sizeof(int) );
for( i= 0; i < numWords; i++ )
{
SaveString( m_vpWords[i]->GetWord(), file );
}
}
void CDictRec::Load( const string &filename )
B.18. DICTREC.CPP 277
{
fstream file;
file.open( filename.c_str(), ios::binary | ios::in );
if( !file.is_open() )
return; // ERROR!!
Load( file );
file.close();
}
void CDictRec::Load( fstream &file )
{
int numWords, i;
string sWord;
CWordRec *pWord;
LoadString( m_sName, file );
LoadString( m_sLangRecFile, file );
m_pLangRec = new CLangRec( m_sLangRecFile );
file.read( (char *)&numWords, sizeof(int) );
m_vpWords.clear();
for( i = 0; i < numWords; i++ )
{
LoadString( sWord, file );
pWord = new CWordRec( sWord, m_pLangRec );
m_vpWords.push_back( pWord );
}
}