Voltar para artigos

Matemática Aplicada & Acústica

Da Teoria das Séries de Fourier à Síntese de Áudio em Python

A música acústica e os sinais digitais compartilham um princípio elementar estabelecido por Jean-Baptiste Joseph Fourier no início do século XIX: qualquer onda periódica pode ser aproximada pela soma ponderada de funções senoidais e cossenoidais.

Essa fundamentação matemática foi o alicerce do meu Trabalho de Conclusão de Curso em Matemática na UFRRJ e deu origem ao projeto MusicaPython.

1. A Formulação Trigonométrica

Seja uma função periódica $f(t)$ com período $T > 0$ e frequência fundamental $f_0 = \frac{1}{T}$. Sob as condições de Dirichlet, sua representação em Série de Fourier é dada por:

$$f(t) = a_0 + \sum_{n=1}^{\infty} \left[ a_n \cos\left(2\pi n f_0 t\right) + b_n \sin\left(2\pi n f_0 t\right) \right]$$

Onde os coeficientes de Fourier medem a intensidade de cada harmônico $n$:

$$a_n = \frac{2}{T} \int_{0}^{T} f(t) \cos(2\pi n f_0 t) \, dt, \quad b_n = \frac{2}{T} \int_{0}^{T} f(t) \sin(2\pi n f_0 t) \, dt$$
O que define o Timbre? Uma nota Lá (440 Hz) tocada em um piano e a mesma nota emitida por um violino possuem rigorosamente a mesma frequência fundamental ($f_0 = 440\text{ Hz}$). A distinção perceptual que nosso ouvido identifica como timbre reside precisamente nas amplitudes relativas dos harmônicos superiores ($n = 2, 3, 4\dots$).

2. Amostragem Digital e Síntese Aditiva

Para sintetizar áudio digitalmente em um computador, discretizamos a variável contínua de tempo $t$ para instantes amostrados $t_k = \frac{k}{F_s}$, onde $F_s$ é a taxa de amostragem padrão (geralmente $44.100\text{ Hz}$):

$$x[k] = \sum_{n=1}^{N} A_n \sin\left( 2\pi n f_0 \frac{k}{F_s} + \phi_n \right)$$

3. Implementação Prática em Python

Com poucas linhas de código utilizando apenas bibliotecas científicas de Python, podemos gerar uma nota musical rica em harmônicos e exportá-la diretamente para um arquivo WAV:

import numpy as np
import wave
import struct

def sintetizar_nota(frequencia=440.0, duracao=2.0, taxa_amostragem=44100):
    t = np.linspace(0, duracao, int(taxa_amostragem * duracao), endpoint=False)
    
    # Síntese aditiva: Fundamental + harmônicos com decaimento exponencial
    sinal = (1.00 * np.sin(2 * np.pi * 1 * frequencia * t) +
             0.50 * np.sin(2 * np.pi * 2 * frequencia * t) +
             0.25 * np.sin(2 * np.pi * 3 * frequencia * t) +
             0.12 * np.sin(2 * np.pi * 4 * frequencia * t))
    
    # Envelope ADSR simplificado (fade-out para evitar estalos)
    envelope = np.linspace(1.0, 0.05, len(sinal))
    sinal = sinal * envelope
    
    # Normalização para áudio de 16 bits (-32767 a +32767)
    sinal_normalizado = (sinal / np.max(np.abs(sinal)) * 32767).astype(np.int16)
    
    with wave.open("nota_la440.wav", "w") as wav_file:
        wav_file.setnchannels(1)      # Mono
        wav_file.setsampwidth(2)      # 2 bytes por amostra (16 bits)
        wav_file.setframerate(taxa_amostragem)
        wav_file.writeframes(sinal_normalizado.tobytes())
    
    print("Áudio sintetizado com sucesso: nota_la440.wav")

if __name__ == "__main__":
    sintetizar_nota()

4. Conclusão e Próximos Passos

A transição da dedução analítica das integrais de Fourier para a sua realização programática em Python ilustra a essência da modelagem matemática e computacional: formular fenômenos físicos com rigor e transformá-los em ferramentas interativas reais.