You are on page 1of 8

El ndice de Transmisin del Habla (STI) para MATLAB

Hay unos pocos algoritmos robustos disponibles para medir objetivamente la inteligibilidad del habla. Comparan lo que
se escucha por un oyente a la informacin que se transmite por la persona que habla, sin embargo, slo uno de estos
algoritmos disponibles obras en las salas reverberantes y en ese momento yo estaba buscando una aplicacin para
MATLAB, no pude encontrar ninguna, nada de nada. Por lo tanto, decid codificar desde cero a m mismo y le mostrar
cmo hacerlo paso a paso.
El ndice de articulacin (AI) es una medicin ms antigua que ha evolucionado hasta el ndice de inteligibilidad del habla
(SII). El SII utiliza una funcin de banda importancia y la banda audible para dar cuenta de la falta de informacin mutua
de enmascaramiento y de diferentes niveles de presentacin de la voz limpia. Tambin existe la corta duracin
inteligibilidad objetivo medida (STOI) que est diseado para la prediccin de tiempo-frecuencia ponderada de voz con
ruido y el ndice de transmisin de voz (STI), que hace uso de la funcin de transferencia de modulacin.
Ahora, esto puede sonar todo un poco confuso, pero en pocas palabras slo el ITS es adecuado para medir la
inteligibilidad en las salas reverberantes. El STI, en esencia, mide la respuesta del canal acstico entre el oyente y la
persona que habla y desde esta medicin del canal se estima la cantidad de informacin mutua podra mantenerse a
travs de ese canal. La razn es razonablemente robusto para diferentes niveles de reverberacin es porque analiza
poderes bandas espectrales usando la funcin de transferencia de modulacin (MTF).
En primer lugar, para medir la ITS necesitaremos la respuesta al impulso (IR) del canal. Esto se puede encontrar
algunas maneras para que las simulaciones y para las grabaciones del mundo real. Algunos de estos pueden ser para
transmitir una seal vlida, grabar el resultado y luego deconvoluir la grabacin con la seal original. Algunas seales
que podran ser utilizados son la secuencia de longitud mxima (MLS), el ruido blanco, barrido sinusoidal, barrido
sinusoidal logartmica, haciendo estallar de un globo o cualquier otra cosa que va explosin. Voy a repasar algunos
mtodos agradables en un futuro post.
Una vez que tenemos una IR vamos a utilizar esto como la entrada a nuestra funcin de MATLAB que se calcule
la ITS. Ir mostrando cmo hacer esto utilizando la versin anterior de la norma (todava bastante exacto para niveles
bajos de presentacin). He utilizado esto como mi definicin de funcin:
function [STI_val, ALcons, STI_val_approx, ALcons_approx] = STI( ImpulseResponse, fs, OctaveFilters )
el cual est listo para algunas caractersticas adicionales que explicar ms adelante.
Ahora, antes de empezar a calcular el ITS queremos establecer una segunda funcin para encontrar la MTF,
porque para encontrar el ITS tenemos que hacer esto muchas veces. Por lo que aade otra definicin de la funcin
despus de la end de palabra clave para la primera funcin y se utiliza esta definicin:
function MTF = getMTF(ir)
Por lo tanto, para encontrar el SMN para una IR dada hay cinco sencillos pasos que tenemos que aadir a la getMTF
funcin. El primer paso es la cuadratura del IR:
ir_2 = ir .^ 2;
Tenemos que conseguir que la energa total del cuadrado IR en algn momento por lo que puede que tambin lo haga
ahora. Esto se puede hacer mediante la integracin de la cual es una suma de los valores discretos de la matriz de IR:
ir_2_int = sum(ir_2);

Despus de esto, necesitamos transformar el cuadrado de infrarrojos en el dominio de la frecuencia. Esto se puede
hacer usando una transformada de Fourier rpida que MATLAB convenientemente tiene una funcin para:
IR_2_fft = fft(ir_2);
Una vez que tenemos nuestra seal en el dominio de frecuencia (espectro sobre) debemos normalizarlo basa en la
energa total se calcul anteriormente:
IR_MTF = IR_2_fft / ir_2_int;
Por ltimo, la MTF queremos volver para su uso en nuestra STI funcin slo debe ser el valor absoluto de la envolvente
del espectro complejo. Esto se puede hacer de esta manera:
MTF = abs(IR_MTF);
Ese es el end de la getMTF funcin.
Ahora podemos empezar a trabajar en encontrar el valor real de las ITS, la parte divertida. Para ello necesitamos
para funcionar todo el IR (IR ancho de banda completo) a travs de filtros de banda de octava y luego encontrar la MTF
de cada una de las seales filtradas. Para realizar un filtrado del IR con filtros de banda de octava debemos crear un filtro
para cada banda de octava, luego filtrar el IR y luego guardar el resultado cada vez. Esto se puede hacer con el cdigo
siguiente:
BandsPerOctave = 1;
N = 6; % Filter Order
F0 = 1000; % Center Frequency (Hz)
f = fdesign.octave(BandsPerOctave,'Class 1','N,F0',N,F0,fs);
F0 = validfrequencies(f);
F0(F0<125)=[]; F0(F0>min([8000,fs/2]))=[]; % Only keep bands in the range required for the STI calculation
Nfc = length(F0);
for i=1:Nfc
if nargin < 3
f.F0 = F0(i);
H = design(f,'butter');
ir_filtered = filter(H, ImpulseResponse);
else
ir_filtered = filter(OctaveFilters(i), ImpulseResponse);
end
MTF_octband(:,i) = getMTF(ir_filtered);
end
Si se lee el cdigo anterior se habrn dado cuenta de la sentencia condicional que dice que si tenemos tres o ms
argumentos de entrada entonces deberamos usar el tercer argumento de entrada, OctaveFilters . He aadido esto
porque la creacin de los filtros de banda de octava es computacionalmente caro, as que si terminamos llamando a
nuestra funcin muchas veces que sera mejor para el clculo de los filtros una vez y reutilizarlos para cada llamada a la
funcin, voy a mostrar cmo se puede hacer esto en un futuro post. Tambin tenga en cuenta la llamada de funcin a
nuestra funcin, getMTF() .
Ahora que tenemos nuestras seales filtradas de banda de octava deberamos obtener la amplitud en diferentes
frecuencias de modulacin. Para el STI hay 14 diferentes frecuencias de modulacin que tienen 1/3 de octava
separacin. Es decir, deberamos obtener la magnitud a las 14 frecuencias especficas de cada MTF. El siguiente cdigo
de lograr esto:

modulation_freqs = (0.63) * ((2.0) .^ ([0:13]./3));


freqs = linspace(0, fs/2, size(MTF_octband,1)/2+1); freqs=freqs(1:end-1);%No nyquist frequency
for i=1:Nfc
for j=1:14
m(i,j) = interp1(freqs,MTF_octband(1:end/2,i),modulation_freqs(j));
end
end
good_freqs = ~any(isnan(m),2);
m(~good_freqs,:)=[];
El ltimo par de lneas aqu son un poco especial, los he aadido por lo que si tenemos cualquier frecuencia que vuelven
malos resultados sern ignoradas.
Los valores de amplitud que tenemos ahora son conocidos como los valores de "m" y el siguiente paso es para
convertirlos en una relacin de "aparente" de seal a ruido (SNR) Esta es la forma en que se lleva a cabo en
MATLAB.:
SNR_apparent = 10*log10( m ./ (1-m) );
El STI dice que hay que limitar la SNR evidente para 15 dB. Lo hice as:
SNR_apparent( SNR_apparent > 15 ) = 15;
SNR_apparent( SNR_apparent < -15 ) = -15;
Una vez que tenemos nuestros SNR aparentes todo lo que tenemos que hacer es encontrar la media de cada una
de las bandas de octava con nuestra matriz de valores aparentes de SNR podemos hacer esto, sencillamente, en
MATLAB.:
SNR_avg = mean(SNR_apparent,2);
En este punto todava tenemos ms de un solo valor STI, sin embargo.
Para obtener estos valores de banda de octava SNR en un solo valor STI tenemos que encontrar una media
ponderada de ellos y luego girar a la media ponderada en un valor entre 0 y 1. La media ponderada significa que
necesitamos saber los diferentes pesos para cada banda, stas se describen en la norma ETS pero si eres inteligente se
puede trabajar a cabo desde el siguiente cdigo:
W = [0.13, 0.14, 0.11, 0.12, 0.19, 0.17, 0.14];
SNR_Wavg = sum(SNR_avg' .* W(good_freqs));
SNR_Wavg_approx = sum(SNR_avg' .* W(good_freqs)/sum(W(good_freqs)));
Tambin he aadido un promedio ponderado que se realiza slo en las buenas frecuencias que hemos definido antes y
lo llam una aproximacin. Esto significa que si, por ejemplo, la frecuencia de muestreo es demasiado bajo y no
disponemos de suficientes bandas de octava para cumplir con el estndar adecuado de las ITS, entonces podemos
todava aproximarse a lo que podra ser el resultado haciendo caso omiso de las bandas que son demasiado altos.
Por supuesto, el cambio de escala que hay que hacer demasiado:
STI_val = (SNR_Wavg + 15) / 30;
STI_val_approx = (SNR_Wavg_approx + 15) / 30;
Eso es! pero, por supuesto, creo que es bueno aadir slo un par de valores de retorno ms convenientes antes de
terminar nuestra funcin:

ALcons = 170.5405 * exp(-5.419*STI_val);


ALcons_approx = 170.5405 * exp(-5.419*STI_val_approx);
Esos clculos son para la prdida de articulacin de consonantes (ALcons) que es otro valor de uso.
Ok, ahora hemos terminado y la funcin se puede finalizar con el end de palabras clave. Si ha hecho todo
correctamente, debe tener cdigo que funciona como la que se puede descargar desde aqu .
Cmo se utiliza la funcin ITS recin creado?
Fcil, slo tiene que pasar la matriz de IR y la frecuencia de muestreo ( fs ) a la funcin:
y=sinc(-7999:8000);
fs=44100;
STIval=STI(y,fs)
Quieres la prdida de articulacin de consonantes en lugar? No hay problema:
[~,ALcons]=STI(y,fs)
Qu pasa si la seal no cumple con los requisitos de muestreo para el estndar ETS? Bueno, vamos a la aproximacin
de las infecciones de transmisin sexual y ALcons y compararlos con los verdaderos valores respetuosos de
normalizacin:
fs=8000;
[STIval,ALcons,STIval_apprx,ALcons_apprx]=STI(y,fs)
Si desea pasar los filtros de octava a la funcin se puede leer sobre este cdigo e intentar escribir una funcin separada
para crearlos. Voy a repasar esto en un prximo post.
Eso es todo, ahora puede pasar a la respuesta al impulso de cualquier canal de audio a travs de esta funcin para
encontrar el valor STI.

The Speech Transmission Index (STI) for MATLAB


There are a few robust algorithms available to objectively measure the intelligibility of speech. They compare what is
heard by a listener to the information that was transmitted by the talker, however, only one of these available algorithms
works in reverberant rooms and at the time I was looking for an implementation for MATLAB, I couldnt find any, zilch. So,
I decided to code it from scratch myself and will show you how to do it step by step.
The articulation index (AI) is an older measure which has evolved to the speech intelligibility index (SII). The SII uses a
band-importance and band-audibility function to account for the lack of mutual information from masking and from varying
presentation levels of the clean speech. There is also the short-time objective intelligibility (STOI) measure which is
designed for the prediction of time-frequency weighted noisy speech and the speech transmission index (STI) which
makes use of the modulation transfer function.
Now, this might all sound a little confusing but in a nutshell only the STI is suitable for measuring intelligibility in
reverberant rooms. The STI in essence measures the response of the acoustic channel between the listener and the
talker and from this measurement of the channel it estimates how much mutual information could be maintained across
that channel. The reason it is reasonably robust to varying reverberation levels is because it analyses spectral band
powers using the modulation transfer function (MTF).
Firstly, to measure the STI we will need the impulse response (IR) of the channel. This can be found a few ways for
simulations and for real-world recordings. Some of these might be to transmit a valid signal, record the result and then
deconvolve the recording with the original signal. Some signals that could be used are maximum length sequence (MLS),
white noise, sine sweep, logarithmic sine sweep, popping of a balloon or anything else that goes bang. I will go over some
nice methods in a future post.
Once we have a valid IR we will use this as the input to our MATLAB function which will compute the STI. I will be
showing how to do this using the older version of the standard (still quite accurate for low presentation levels). I used this
as my function definition:
function [STI_val, ALcons, STI_val_approx, ALcons_approx] = STI( ImpulseResponse, fs, OctaveFilters )
which is ready for some extra features which I will explain later.
Now, before we start to calculate the STI we want to set up a second function to find the MTF because to find the
STI we must do this many many times. So I added another function definition after the end keyword for the first function
and used this definition:
function MTF = getMTF(ir)
So, to find the MTF for a given IR there are five simple steps which we need to add to the getMTF function. The first step
is to square the IR:
ir_2 = ir .^ 2;
We need to get the total energy of the squared IR at some point so may as well do it now. This can be done by integrating
which is a summation of the discrete values of the IR array:
ir_2_int = sum(ir_2);

After this we need to transform the squared IR into the frequency domain. This can be done using a fast Fourier transform
which MATLAB conveniently has a function for:
IR_2_fft = fft(ir_2);
Once we have our frequency domain signal (the envelope spectrum) we should normalise it based on the total energy we
calculated earlier:
IR_MTF = IR_2_fft / ir_2_int;
Lastly, the MTF we want to return for use in our STI function should only be the absolute value of the complex envelope
spectrum. This can be done like so:
MTF = abs(IR_MTF);
That is the end of the getMTF function.
Now we can get started on finding the actual STI value, the fun part. For this we need to run the entire IR (the full
bandwidth IR) through octave band filters and then find the MTF of each of the filtered signals. To filter the IR with octave
band filters we must create a filter for each octave band, then filter the IR and then save the result each time. This can be
done with the following code:
BandsPerOctave = 1;
N = 6; % Filter Order
F0 = 1000; % Center Frequency (Hz)
f = fdesign.octave(BandsPerOctave,'Class 1','N,F0',N,F0,fs);
F0 = validfrequencies(f);
F0(F0<125)=[]; F0(F0>min([8000,fs/2]))=[]; % Only keep bands in the range required for the STI calculation
Nfc = length(F0);
for i=1:Nfc
if nargin < 3
f.F0 = F0(i);
H = design(f,'butter');
ir_filtered = filter(H, ImpulseResponse);
else
ir_filtered = filter(OctaveFilters(i), ImpulseResponse);
end
MTF_octband(:,i) = getMTF(ir_filtered);
end
If you read the code above you may have noticed the conditional statement that says that if we have three or more input
arguments then we should use the third input argument, OctaveFilters. I have added this because creating the octave
band filters is computationally expensive, so if we end up calling our function many times it would be better to calculate
the filters once and reuse them for each function call, I will show how this can be done in a future post. Also note the
function call to our function, getMTF().
Now that we have our octave band filtered signals we should get the amplitude at different modulation
frequencies. For the STI there are 14 different modulation frequencies which have 1/3 octave spacing. That is to say, we
should get the magnitude at 14 specific frequencies from each MTF. The following code will accomplish this:
modulation_freqs = (0.63) * ((2.0) .^ ([0:13]./3));
freqs = linspace(0, fs/2, size(MTF_octband,1)/2+1); freqs=freqs(1:end-1);%No nyquist frequency
for i=1:Nfc

for j=1:14
m(i,j) = interp1(freqs,MTF_octband(1:end/2,i),modulation_freqs(j));
end
end
good_freqs = ~any(isnan(m),2);
m(~good_freqs,:)=[];
The last couple of lines here are a little bit special, I have added them so that if we have any frequencies which return bad
results they will be ignored.
The amplitude values we now have are known as the m values and the next step is to convert them into an
apparent signal-to-noise ratio (SNR). This is how it is done in MATLAB:
SNR_apparent = 10*log10( m ./ (1-m) );
The STI says we should limit the apparent SNR to 15dB. I did this like so:
SNR_apparent( SNR_apparent > 15 ) = 15;
SNR_apparent( SNR_apparent < -15 ) = -15;
Once we have our apparent SNRs all we need to do is find the average for each of the octave bands. With our
matrix of apparent SNR values we can do this quite simply in MATLAB:
SNR_avg = mean(SNR_apparent,2);
At this point we still have more than a single STI value, though.
To get these octave band SNR values into a single STI value we need to find a weighted average of them and
then turn that weighted average into a value between 0 and 1. The weighted average means we need to know the
different weights for each band, these are described in the STI standard but if youre clever you can work them out from
the following code:
W = [0.13, 0.14, 0.11, 0.12, 0.19, 0.17, 0.14];
SNR_Wavg = sum(SNR_avg' .* W(good_freqs));
SNR_Wavg_approx = sum(SNR_avg' .* W(good_freqs)/sum(W(good_freqs)));
I have also added a weighted average which is performed on only the good frequencies which we defined before and
called it an approximation. This means that if, for instance, the sampling frequency is too low and we dont have enough
octave bands to fulfill the proper STI standard, then we can still approximate what the result might be by ignoring the
bands that are too high.
Of course, the rescaling needs to be done too:
STI_val = (SNR_Wavg + 15) / 30;
STI_val_approx = (SNR_Wavg_approx + 15) / 30;
Thats it! but of course, I think it is nice to add just a couple more convenient return values before we end our function:
ALcons = 170.5405 * exp(-5.419*STI_val);
ALcons_approx = 170.5405 * exp(-5.419*STI_val_approx);
Those calculations are for the articulation loss of consonants (ALcons) which is another useful value.

Ok, now we are done and the function can be ended with the end keyword. If you have done it all properly you should
have code that works like the one you can download from here.
How do you use your newly created STI function?
Easy, just pass your IR array and the sampling frequency (fs) to the function:
y=sinc(-7999:8000);
fs=44100;
STIval=STI(y,fs)
Want the articulation loss of consonants instead? No problem:
[~,ALcons]=STI(y,fs)
What if the signal doesnt meet the sampling requirements for the STI standard? Well, lets approximate the STI and
ALcons and compare them with the true standard-abiding values:
fs=8000;
[STIval,ALcons,STIval_apprx,ALcons_apprx]=STI(y,fs)
If youd like to pass the octave filters to the function you can read over this code and try write a separate function to create
them. I will go over this in a future post.
Thats it, you can now pass the impulse response of any audio channel through this function to find the STI value.

You might also like