Na Ć”rea de reconhecimento automĆ”tico de voz, uma das primeiras etapas estĆ” no processo de extraĆ§Ć£o de caracterĆsticas dos sinais de voz, ou seja, identificar os componentes do sinal de Ć”udio que sĆ£o bons para identificar o conteĆŗdo linguĆstico. E uma dessas tĆ©cnicas de reconhecimento de voz estĆ” o Mel-Frequency Cepstral Coefficients (MFCCs), a qual foi introduzida na dĆ©cada de 1980 por Davis e Mermelstein e tem sido considerada o estado da arte atĆ© o momemento [1].
Os MFCCs sĆ£o comumente obtidos atravĆ©s dos seguintes passos:
A operaĆ§Ć£o do MFCC foi implementado para a tarefa 2 da disciplina de Fundamentos de ComputaĆ§Ć£o GrĆ”fica (FCG), a linguagem de programaĆ§Ć£o escolhida foi o R. O cĆ³digo encontra-se disponĆvel no github.
devtools
contido no CRAN executando o seguinte comando no console do R: install.packages("devtools")
.install.packages('ggplot2')
).install.packages('tuneR')
).devtools::install_github("JessicaSousa/MFCC")
no console do R.ApĆ³s efetuar a instalaĆ§Ć£o do pacote, para usĆ”-lo basta utilizar a seguinte funĆ§Ć£o library(MFCC)
. Com o comando lsf("package:MFCC")
Ć© possĆvel obter uma lista contendo o nome das funƧƵes implementadas, conforme ilustrado abaixo:
#> [1] "%>%" "apply_dct"
#> [3] "apply_lifter" "apply_preemphasis"
#> [5] "apply_window_hamming" "calcule_filter_bank"
#> [7] "compute_mel_filterbanks" "compute_power_spectrum"
#> [9] "convert_hertz_to_mel" "convert_mel_to_hertz"
#> [11] "dct1" "dct2"
#> [13] "dct3" "dct4"
#> [15] "filter_bank_function" "frame_the_signal"
#> [17] "kronecker_delta" "mfcc_function"
#> [19] "nfft" "sound.data"
Para saber informaƧƵes sobre alguma funĆ§Ć£o, pode ser utilizado help('nome da funĆ§Ć£o')
ou ?nome da funĆ§Ć£o
.
Alguns exemplos do uso do pacote sĆ£o ilustrados a seguir:
library(MFCC) #carregar pacote do MFCC
library(ggplot2) #biblioteca para grƔficos
#Obter arquivo de Ɣudio exemplo
sound.data <- MFCC::sound.data
#Com o pacote tuneR, pode-se carregar um arquivo do disco da seguinte forma: (descomentar abaixo)
#sound.data <- tuneR::readWave('audio.wav', from = 0, to = 3.5, units = "seconds")
sound <- sound.data@left #valores do arquivo de Ɣudio
sample.rate <- sound.data@samp.rate #sample rate do arquivo de Ɣudio
#Exibir arquivo de Ɣudio
sound.time <- 0:(length(sound)-1)/sample.rate #tempo em segundos
#criar estrutura contendo o x e y do grƔfico
data.raw <- data.frame(x = sound.time, y = sound)
#Exibir grƔfico
p <- ggplot(data.raw, aes(x, y)) + geom_line() +
xlab("Tempo (s)") + ylab("Amplitude") #+
Inicialmente, sĆ£o definidos alguns parĆ¢metros arbitrĆ”rios que sĆ£o necessĆ”rios para o cĆ”lculo das MFCCs, sĆ£o eles:
fft.npoints
), geralmente Ć© realizada uma FFT de 512 pontos.freq.lower
e freq.lower
, respectivamente)num.filters
), consiste em um conjunto de valores de 20-40.A seguir sĆ£o inicializadas as respectivas variĆ”veis.
fft.npoints <- 512 #nĆŗmeros de pontos considerados para o cĆ”lculo da fft
#help(nfft) #para mais informaƧƵes
freq.lower <- 0 #frequĆŖncia mĆnima em hertz considerada
freq.upper <- sample.rate / 2 #frequĆŖncia mĆ”xima em hertz considerada
num.filters <- 40 #nĆŗmero filtros considerados para o filterbank
Antes de aplicar os passos da MFCCs, Ć© aplicado um filtro de prĆ©-ĆŖnfase sobre o sinal, com o objetivo de amplificar as altas frequĆŖncias. A aplicaĆ§Ć£o desse filtro pode ser obtida a partir da seguinte equaĆ§Ć£o:
\[y(t) = x(t) - \alpha x(t-1)\]
Esse filtro foi implementado sobre o nome apply_preemphasis
, os valores padrƵes de \(\alpha\) sĆ£o 0,95 ou 0,97. A seguir Ć© ilustrado o resultado da aplicaĆ§Ć£o do filtro sobre o sinal de som de entrada.
emphasized_signal <- apply_preemphasis(sound, 0.97)
#criar estrutura contendo o x e y do grƔfico
data.emphasized <- data.frame(x = sound.time, y = emphasized_signal)
#ExibiĆ§Ć£o
p <- ggplot(data.emphasized, aes(x, y)) + geom_line() +
xlab("Tempo (s)") + ylab("Amplitude")
As etapas do algoritmo dos MFCCs sĆ£o descritas juntamente com as chamadas de funƧƵes implementadas neste pacote:
frames <- frame_the_signal(emphasized_signal, sample.rate)#1.Dividir o sinal em short frames.
frames <- apply_window_hamming(frames) #aplicar a funĆ§Ć£o de hamming para cada frame
power.frames <- compute_power_spectrum(frames, n = fft.npoints)
fbanks <- compute_mel_filterbanks(freq.lower,freq.upper, num.filters, fft.npoints, sample.rate)
#Para calcular a energia do filter bank, multiplica-se cada filter bank com seus power spectrum.
filter.banks <- power.frames %*% t(fbanks)
#substituir os zeros para evitar problemas com log
filter.banks[filter.banks == 0] <- .Machine$double.eps
filter.banks <- 20 * log10(filter.banks)
#Para cada FilterBank Ć© aplicada a operaĆ§Ć£o de Discrete Cosine Transform (DCT).
mfcc <- t(apply(filter.banks, 1, function(x) apply_dct(x)))
mfcc <- mfcc[, 2:13]
A visualizaĆ§Ć£o dos filterbanks pode ser vista com o seguinte trecho de cĆ³digo:
#Organizar dado para melhor visualizaĆ§Ć£o
x <- seq(from = freq.lower, to = freq.upper, length.out = ncol(fbanks)) %>% rep(num.filters)
y <- t(fbanks)
y %<>% as.data.frame() %>% tidyr::gather()
data <- data.frame(x = x, values = y$value, filters = y$key)
#Exibir espectograma
p <- ggplot(data, aes(x, values, colour = filters)) +
geom_line() + xlab("FrequĆŖncia") + ylab("Amplitude") + theme(legend.position="none") +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0))
#Organizar dado para melhor visualizaĆ§Ć£o
fbanks.spec <- reshape2::melt(filter.banks)
fbanks.spec$Var1 <- fbanks.spec$Var1 / 100
fbanks.spec$Var2 <- fbanks.spec$Var2 / 10
#Exibir espectograma
p <- ggplot(fbanks.spec, aes(Var1,Var2)) + geom_raster(aes(fill = value)) +
scale_fill_gradientn(colours = rainbow(10)) +
xlab("Tempo (s)") + ylab("FrequĆŖncia (kHz)") + ggtitle("Espectograma do sinal") +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0))
\[\hat{MFCC_i} = 1 + (\frac{w_i D}{2})\sin(\frac{Ļ n}{D})\]
E estĆ” implementado sobre a seguinte funĆ§Ć£o apply_lifter
:
#Aplicar o sinusoidal liftering aos MFCCs
mfcc.lift <- apply_lifter(mfcc)
#Organizar dado para melhor visualizaĆ§Ć£o
mfccs.spec <- reshape2::melt(mfcc.lift)
mfccs.spec$Var1 <- mfccs.spec$Var1 / 100
#Espectograma do MFCCs
p <- ggplot(mfccs.spec, aes(Var1,Var2, fill=value)) + geom_raster(aes(fill = value)) +
scale_fill_gradientn(colours = rainbow(10)) +
xlab("Tempo (s)") + ylab("Coeficientes das MFCCs") + ggtitle("MFCCs") +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0))