Um segundo de áudio em qualidade de CD são 44.100 números. Um minuto, 2,6 milhões. Jogar isso direto num classificador é tão inútil quanto jogar os pixels crus de uma foto: a informação está lá, mas espalhada de uma forma que nenhum modelo razoável consegue aproveitar.

O que torna o áudio diferente de outros dados é que a informação perceptualmente relevante não está na amplitude ao longo do tempo — está na distribuição de energia entre frequências, e em como essa distribuição muda. Duas pessoas dizendo a mesma palavra produzem formas de onda completamente diferentes e espectros muito parecidos.

Esta aula percorre a cadeia padrão de representação de sinal: do domínio do tempo para o da frequência, do espectro para o espectrograma, e do espectrograma para os coeficientes cepstrais. É a mesma cadeia que serve a voz, música, vibração de máquinas e sinais biomédicos.

Transformar sinais do domínio do tempo para o da frequência com a Transformada de Fourier, construir espectrogramas com STFT, extrair MFCCs e características espectrais, e escolher a representação adequada ao tipo de tarefa.

Fourier: da onda ao espectro

A Transformada de Fourier parte de uma ideia forte: qualquer sinal periódico pode ser escrito como soma de senoides de frequências, amplitudes e fases diferentes. Aplicá-la responde à pergunta "quais frequências compõem este sinal, e com que intensidade?".

import numpy as np

def espectro(sinal, taxa_amostragem):
    """Magnitude por frequência de um sinal real."""
    n = len(sinal)
    janela = np.hanning(n)                    # reduz o vazamento espectral
    transformada = np.fft.rfft(sinal * janela)
    frequencias = np.fft.rfftfreq(n, d=1 / taxa_amostragem)
    return frequencias, np.abs(transformada)

Dois detalhes que a fórmula não mostra:

  • rfft em vez de fft. Para sinal real, o espectro é simétrico; usar a versão real economiza metade da computação e evita ter que descartar a metade espelhada.
  • A janela de Hanning. A FFT assume que o trecho analisado se repete periodicamente. Como o início e o fim raramente casam, surge uma descontinuidade artificial que espalha energia por todo o espectro — o vazamento espectral. Multiplicar por uma janela que vai a zero nas bordas elimina o problema.

Há um limite físico a respeitar: o teorema da amostragem diz que só é possível representar frequências até metade da taxa de amostragem. Áudio a 16 kHz captura no máximo 8 kHz — suficiente para voz, insuficiente para música.

O problema do espectro: o tempo some

A Transformada aplicada ao sinal inteiro diz quais frequências existem, mas não quando. Uma gravação em que alguém diz "sim" e depois "não" produz um espectro parecido com o da ordem inversa.

Para a maioria das tarefas, isso é fatal. A solução é a Transformada de Fourier de Tempo Curto (STFT): em vez de analisar tudo de uma vez, divide-se o sinal em janelas curtas e sobrepostas, e calcula-se o espectro de cada uma.

import librosa
import numpy as np

sinal, sr = librosa.load("audio.wav", sr=16000)

stft = librosa.stft(sinal, n_fft=512, hop_length=160, win_length=400)
magnitude = np.abs(stft)
espectrograma_db = librosa.amplitude_to_db(magnitude, ref=np.max)

Com sr = 16000, esses parâmetros significam: janelas de 25 ms (win_length=400), avançando 10 ms por vez (hop_length=160). Não é arbitrário — é o padrão histórico em processamento de fala, porque 25 ms é curto o bastante para o sinal de voz ser aproximadamente estacionário e longo o bastante para resolver as frequências que interessam.

E aqui está o compromisso central de toda análise tempo-frequência: janela longa dá boa resolução em frequência e ruim em tempo; janela curta, o inverso. Não há como ter as duas — é uma limitação matemática, não de implementação. Para detectar um clique breve, janela curta. Para distinguir duas notas próximas, janela longa.

A escala mel: ouvir como um humano ouve

A percepção humana de altura não é linear em frequência. A diferença entre 200 Hz e 300 Hz soa enorme; entre 8.000 Hz e 8.100 Hz, imperceptível — embora as duas diferenças sejam de 100 Hz.

A escala mel é uma transformação que comprime as altas frequências, aproximando essa percepção. Um espectrograma mel redistribui a energia em bandas espaçadas segundo essa escala:

mel = librosa.feature.melspectrogram(
    y=sinal, sr=sr,
    n_fft=512, hop_length=160,
    n_mels=64,           # 40 a 128 é a faixa usual
    fmin=50, fmax=8000,  # descarta o que não interessa
)
mel_db = librosa.power_to_db(mel, ref=np.max)

O ganho prático é grande: 257 bins de frequência viram 64 bandas mel, com pouca perda de informação perceptualmente relevante. É a entrada padrão das redes convolucionais aplicadas a áudio — a CNN trata o espectrograma mel como uma imagem, e funciona notavelmente bem.

MFCC: descorrelacionar e comprimir

Os Coeficientes Cepstrais nas Frequências Mel vão um passo além. A receita: espectrograma mel → logaritmo → Transformada Discreta do Cosseno → guardar os primeiros coeficientes.

mfcc = librosa.feature.mfcc(y=sinal, sr=sr, n_mfcc=13, n_mels=64)

# as derivadas capturam a dinâmica — como o som muda
delta  = librosa.feature.delta(mfcc)
delta2 = librosa.feature.delta(mfcc, order=2)

caracteristicas = np.vstack([mfcc, delta, delta2])   # 39 x quadros

Por que essa sequência esquisita funciona:

  • O logaritmo separa, numa soma, o que na origem era um produto: a fonte sonora (as cordas vocais) e o filtro (o trato vocal). Isso permite isolar a parte que identifica o fonema.
  • A DCT descorrelaciona as bandas, que são fortemente correlacionadas entre si, e concentra a informação nos primeiros coeficientes.
  • Guardar 13 descarta o detalhe fino e retém a envoltória espectral — a forma geral do espectro, que é o que distingue um fonema de outro.

As deltas merecem ênfase: elas representam a variação dos coeficientes ao longo do tempo. Sozinhos, os MFCCs de um quadro descrevem um instante congelado; a fala é movimento, e boa parte da informação está na transição.

Uma observação de contexto: os MFCCs dominaram reconhecimento de fala por décadas, mas redes profundas modernas frequentemente preferem o espectrograma mel cru — a descorrelação da DCT descarta informação que a rede saberia aproveitar. Para modelos clássicos e conjuntos pequenos, MFCC continua excelente; para redes profundas com muito dado, teste o mel antes.

Características espectrais interpretáveis

Nem toda tarefa precisa de espectrograma. Para classificar tipos de som, detectar falha em máquina ou segmentar música, um punhado de descritores escalares por quadro costuma bastar — e são interpretáveis:

def caracteristicas_espectrais(sinal, sr):
    return {
        # "brilho": onde está o centro de massa do espectro
        "centroide":  librosa.feature.spectral_centroid(y=sinal, sr=sr).mean(),
        # largura da distribuição de energia
        "largura":    librosa.feature.spectral_bandwidth(y=sinal, sr=sr).mean(),
        # frequência abaixo da qual está 85% da energia
        "rolloff":    librosa.feature.spectral_rolloff(y=sinal, sr=sr).mean(),
        # quão ruidoso vs. tonal
        "planicidade": librosa.feature.spectral_flatness(y=sinal).mean(),
        # taxa de cruzamentos por zero: alta em ruído e consoantes fricativas
        "cruzamentos": librosa.feature.zero_crossing_rate(sinal).mean(),
        # energia
        "rms":        librosa.feature.rms(y=sinal).mean(),
    }

O centroide espectral corresponde bem à sensação de "brilho": um prato de bateria tem centroide alto, um bumbo tem baixo. A planicidade espectral distingue som tonal (energia concentrada em harmônicos) de ruído (energia espalhada) — é o descritor que detecta chiado, vazamento e boa parte das falhas mecânicas.

Agregar quadros: o passo que define a arquitetura

Um espectrograma tem uma coluna por quadro, e diferentes gravações têm durações diferentes. Modelos clássicos exigem entrada de tamanho fixo, então é preciso agregar.

A agregação ingênua é a média — e ela joga fora a dinâmica:

# frágil: dois sons com a mesma média e evoluções opostas ficam idênticos
vetor = mfcc.mean(axis=1)

Agregar com mais estatísticas preserva bem mais:

def agregar(matriz):
    """Estatísticas por coeficiente, preservando parte da dinâmica."""
    return np.concatenate([
        matriz.mean(axis=1), matriz.std(axis=1),
        matriz.min(axis=1),  matriz.max(axis=1),
        np.percentile(matriz, 25, axis=1), np.percentile(matriz, 75, axis=1),
    ])

A alternativa estrutural é não agregar: manter o espectrograma como matriz e usar um modelo que lida com sequência ou imagem — CNN sobre o mel, ou modelos de sequência. É o caminho com mais dados e mais computação, e o que produz os melhores resultados hoje.

Armadilhas específicas de áudio

Vazamento por gravação. Se trechos do mesmo arquivo aparecem em treino e teste, o modelo aprende a reconhecer a gravação — o microfone, o ruído de fundo, a acústica da sala — e não a classe. A divisão precisa ser por gravação ou por falante, nunca por trecho.

Normalizar sem cuidado. Normalizar amplitude por arquivo é geralmente bom (remove a diferença de volume de gravação). Normalizar cada banda de frequência por arquivo pode apagar exatamente a diferença entre as classes.

Ignorar a taxa de amostragem. Misturar arquivos a 44,1 kHz e 16 kHz sem reamostrar produz características incomparáveis — a mesma frequência cai em bins diferentes. Padronize a taxa na entrada do pipeline.

Aumentação que muda a classe. Alterar o tom (pitch shift) é aumentação útil em classificação de ambiente e destrutiva em identificação de falante, onde o tom é justamente o sinal. Toda aumentação precisa preservar o rótulo.

A informação relevante em áudio está na distribuição de energia por frequência, não na amplitude ao longo do tempo. A FFT entrega o espectro mas perde o tempo; a STFT resolve isso analisando janelas curtas e sobrepostas — 25 ms com passo de 10 ms é o padrão em fala. O compromisso é incontornável: janela longa dá boa resolução em frequência e ruim em tempo, e vice-versa. Use rfft para sinal real e sempre uma janela como a de Hanning, para evitar vazamento espectral. A escala mel comprime as altas frequências e aproxima a percepção humana, e o espectrograma mel é a entrada padrão de CNNs em áudio. O MFCC é mel, log, DCT e os treze primeiros coeficientes, com as deltas capturando a dinâmica e valendo, na prática, tanto quanto os próprios coeficientes. Vale saber que redes profundas modernas costumam preferir o mel cru ao MFCC, porque a DCT descarta informação que a rede aproveitaria. Descritores espectrais — centroide, planicidade, rolloff, cruzamentos por zero — são interpretáveis e bastam em muitas tarefas. Agregar quadros só pela média joga fora a dinâmica: use várias estatísticas, ou mantenha a matriz e entregue a uma CNN. E na hora de dividir, divida por gravação ou por falante, padronize a taxa de amostragem e confira se a aumentação preserva o rótulo.

Exercícios

  1. Por que a FFT aplicada ao áudio inteiro é inadequada para reconhecer palavras faladas?

    ✓ Resposta:

    Porque ela devolve a composição de frequências do sinal inteiro, sem qualquer informação sobre quando cada frequência ocorreu. Uma gravação de "sim, não" e outra de "não, sim" contêm essencialmente os mesmos conteúdos espectrais e produzem espectros muito parecidos, embora sejam enunciados diferentes. Como a fala é definida pela sequência de sons, essa perda é fatal. A solução é a STFT: dividir o sinal em janelas curtas e sobrepostas e calcular o espectro de cada uma, produzindo uma representação bidimensional — tempo no eixo horizontal, frequência no vertical — que preserva a ordem dos eventos.

  2. Explique o compromisso entre resolução temporal e espectral e como escolher a janela.

    ✓ Resposta:

    A resolução em frequência de uma janela é aproximadamente a taxa de amostragem dividida pelo número de amostras da janela: janelas mais longas contêm mais ciclos de cada senoide e distinguem frequências próximas com mais precisão. Mas uma janela longa cobre um intervalo maior de tempo, então qualquer evento dentro dela é "borrado" — perde-se precisão sobre quando aconteceu. Janelas curtas invertem o quadro. É uma limitação matemática (relacionada ao princípio da incerteza aplicado a sinais), não um defeito de implementação. A escolha segue a tarefa: para detectar eventos breves — um clique, um transiente de percussão, um impacto mecânico —, janela curta; para separar componentes de frequência próximas — duas notas vizinhas, harmônicos —, janela longa. Em fala, 25 ms é o meio-termo consagrado.

  3. Qual o papel do logaritmo e da DCT no cálculo dos MFCCs?

    ✓ Resposta:

    O logaritmo converte em soma o que na origem é um produto. A produção de voz é modelada como uma fonte (a vibração das cordas vocais) passando por um filtro (o trato vocal), e no domínio da frequência isso é uma multiplicação; tomando o log, os dois componentes passam a se somar, tornando-os separáveis. Ele também comprime a faixa dinâmica, aproximando a percepção humana de intensidade, que é aproximadamente logarítmica. A DCT atua sobre as bandas mel, que são fortemente correlacionadas entre si (bandas vizinhas carregam energia parecida): ela descorrelaciona essas bandas e concentra a informação nos primeiros coeficientes. É isso que permite guardar apenas 13 valores retendo a envoltória espectral — a forma geral do espectro, que caracteriza o fonema — e descartar o detalhe fino, associado à fonte e ao ruído.

  4. Por que dividir treino e teste por trecho de áudio, e não por gravação, invalida a avaliação?

    ✓ Resposta:

    Porque trechos da mesma gravação compartilham características que nada têm a ver com a classe: a resposta em frequência daquele microfone, o ruído de fundo daquele ambiente, a acústica da sala, o volume da gravação, o timbre daquele falante. Com trechos do mesmo arquivo dos dois lados da divisão, o modelo pode atingir excelente desempenho reconhecendo a gravação e associando-a ao rótulo, sem ter aprendido nada sobre o fenômeno de interesse. Em produção, diante de uma gravação nova, o desempenho despenca. A divisão correta é por unidade independente — gravação, sessão ou falante, conforme a tarefa —, garantindo que nenhuma informação da fonte atravesse a fronteira.

  5. Você vai classificar falantes. pitch shift é uma boa aumentação? E se a tarefa fosse detectar tipo de ambiente?

    ✓ Resposta:

    Para identificação de falante, não — é destrutiva. A frequência fundamental da voz é uma das características mais discriminantes entre pessoas, e deslocá-la altera exatamente o atributo que define o rótulo: o áudio aumentado pode soar como outro falante, e o modelo aprende a ignorar o sinal mais útil que tem. Aumentações adequadas ali preservam a identidade vocal: ruído de fundo, variação de volume, simulação de reverberação, codecs de compressão. Para classificação de ambiente (rua, escritório, restaurante), um deslocamento moderado de tom é aceitável, porque a classe é definida pela textura sonora do ambiente e não por uma frequência fundamental específica — ainda assim vale manter o deslocamento pequeno, já que muitos sons ambientais têm assinatura espectral característica. O princípio geral: toda aumentação precisa preservar o rótulo, e isso depende da tarefa, não da técnica.

Fontes e leituras recomendadas