Notas Musicais Artificiais (Revisitado — Parte 1)
Há quase 10 anos desde que eu gravei o meu primeiro vídeo para o YouTube. Era despretensioso, eu só queria testar um software de gravação de tela e compartilhar um pouco dos projetos recentes que eu havia realizado usando MATLAB.
O vídeo que eu produzi em 2011 foi nomeado de “Gerador de notas musicais (Matlab tutorial)” (mostrando um pouco da minha confusão mental misturando português e inglês).
Após muito tempo procrastinando, resolvi voltar a produzir conteúdo. Muita coisa mudou desde 2011 (mas MUITA coisa mesmo). Assim como eu também mudei bastante, e acredito que tenho algumas coisas novas (e antigas também) a compartilhar.
Hoje eu vou revisitar o meu tutorial de geração de notas musicais artificiais. É um remake, na verdade. Dessa vez, vou usar Python. Também vou escrever o código de um jeito bem diferente do vídeo original, para que esse código possa ser reusado e cresça em um projeto mais interessante.
Vem junto se você quer ver o primeiro passo desse desafio.
- Um copo de Fourier e duas doses de numpy, por favor.
A música é composta por uma sequência estruturada e muito bem planejada de sons. O som, por sua vez, é uma onda mecânica, um efeito de variação de pressão transmitido molécula por molécula através do ambiente. O som é percebido por nós, seres humanos, através dos nossos tímpanos, mas apenas dentro de uma faixa limitada de frequências.
Para representar uma onda, matematicamente, usamos funções trigonométricas. A função seno é capaz de gerar um sinal periódico com alguns parâmetros bem definidos: amplitude, frequência e fase.
Em Python, vamos usar a biblioteca numpy para nos auxiliar com algumas manipulações matemáticas e produzir os sinais de interesse.
Executando o código anterior, você vai observar a seguinte saída no gráfico:
Bom, talvez não fosse isso que você esperava. Mas essa é a nossa primeira realização de um sinal sonoro com frequência de 2 Hertz, amplitude 10 (a unidade não importa muito no momento) e fase 0 graus. Note que eu mostrei o sinal por dois períodos inteiros, ou seja, a onda se repete duas vezes no intervalo entre 0 e 1 segundo. Isso porque a frequência f (número de vezes que a onda se repete por segundo) e o período T (duração de um único ciclo da onda) possuem uma relação definida pelo inverso um do outro, ou seja,
Mas por que a onda não parece senoidal? Bem, ainda não introduzimos o conceito do tempo de amostragem do sinal. Uma coisa é representar um sinal contínuo em uma equação. Outra coisa é digitalizar isso para que o computador consiga entender.
Todo sinal que geramos artificialmente no computador é um sinal digital.
Para que um sinal contínuo seja propriamente recuperado, devemos amostrá-lo (isso significa literalmente “anotar seus pontos de tempo em tempo”) adequadamente. Nyquist mostrou que é necessária uma frequência de amostragem maior ou igual a duas vezes a maior frequência presente no sinal que desejamos recuperar. No caso, nossa onda de 2 Hz precisa ser amostrada com pelo menos 4 Hz. Isso significa coletar amostras a cada 1/4 (ou seja, 0,25) segundos!
Mas se você observar no nosso código, eu usei um passo de amostragem ainda menor na hora de gerar o vetor dos tempos de observação: 0,1 segundos. Mesmo assim, não ficou muito bonito. Bem, o fato é que existe uma diferença entre “o necessário para recuperar o sinal” e “o adequado para visualizar o sinal”. Para fins de visualização, talvez seja desejável ver muitos pontos dentro do período, observando cada pequena variação da amplitude do som que está sendo produzido. Portanto, em vez de amostrar com apenas com a frequência mínima (que garante 2 pontos por período), talvez seja importante amostrar com 10 ou até 100 vezes mais pontos.
O trecho de código a seguir introduz algumas variáveis novas relacionadas ao tempo de duração do sinal e ao tempo de amostragem. Aproveito também para mexer um pouco na fase do sinal e ver o efeito disso no gráfico.
Agora, ao executar o código anterior, você vai obter o seguinte resultado na imagem de saída:
Essa onda parece muito mais bonita agora, não? Podemos ver as transições suaves entre cada ponto, observando sua amplitude diminuir e aumentar periodicamente. Note que o deslocamento de fase transformou o nosso seno em uma onda adiantada por 1/4 do período (começando da amplitude máxima em vez da amplitude nula, como era antes). Tecnicamente, agora ela é uma onda “cosseno”.
Você sabia que qualquer forma de onda na verdade é uma combinação de senos e cossenos? Essa interpretação foi introduzida por Joseph Fourier (1768 — 1830), uma matemático e físico Francês. As transformadas matemáticas elaboradas por Fourier são usadas até hoje, e são fundamentais nas áreas de engenharia, de processamento de sinais e, é claro, estão presentes na música também!
Voltaremos a falar mais sobre Fourier no futuro, mas é minha obrigação recomendar este aplicativo desenvolvido por Paul Falstad (programador Java), através do qual ele mostra de um jeito muito didático e visual a interpretação da composição de sinais através da soma de senos e cossenos.
Gatos versus MP3
Para finalizar este primeiro passo do nosso projeto, vamos fazer alguns últimos ajustes. Antes de tentarmos reproduzir o som gerado pela nossa onda, é importante observarmos alguns pontos.
Nós, seres humanos, temos a capacidade auditiva de ouvir sons dentro de uma faixa com limites aproximados de 20 Hz (frequência mais baixa, som grave) e 22 kHz (frequência mais alta, som agudo). Outros animais possuem uma faixa auditiva diferente — gatos, por exemplo, conseguem escutar frequências de 55 Hz até 79 kHz.
É por causa da faixa auditiva dos seres humanos que a frequência de amostragem de arquivos de áudio mais comum seja 44,1 kHz (lembra do Nyquist?). Mas com base nos nossos exemplos anteriores, já podemos entender que frequências muito agudas (próximas de 22 kHz) sejam mais prejudicadas ou distorcidas. Por isso, existem alguns arquivos que usam taxas de amostragem mais altas, como 96 kHz ou até 192 kHz. Entretanto, observe que quanto mais amostras coletarmos, maior será a memória ocupada por esses arquivos de áudio. Mas se você quiser agradar o(a) seu(sua) gato(a), ligue a alta fidelidade de som no Spotify!
É importante observar também que nossa capacidade auditiva pode variar de acordo com diversos fatores: sexo, idade, deficiências congênitas, deficiências desenvolvidas, etc. Em geral, existe uma resposta média do ouvido humano que revela que nossa audição é mais sensível a sons de 2 kHz a 5 kHz, aproximadamente, com um pico de sensibilidade em 3,5 kHz. Não é à toa que a telefonia otimizou vários sistemas para transmissão de sinais de voz entre 300 Hz e 3400 Hz, amostrando nossa fala em 8 kHz (veja mais sobre a codificação PCM)! Os próprios alto-falantes que usamos possuem respostas em frequência que costumam falhar para sinais de áudio muito agudos ou até mesmo muito graves, em alguns casos, pois são otimizados para a nossa faixa auditiva mais sensível.
Esse contexto todo é para justificar as seguintes modificações que precisamos fazer no nosso código: precisamos gerar ondas dentro da faixa audível e usar uma frequência de amostragem fixa e típica dos padrões de áudio digital!
O código a seguir realiza essas modificações. Escolhi usar uma frequência de amostragem fixa de 44,1 kHz. Vamos gerar uma onda com frequência de 440 Hz (a mesma do diapasão, instrumento de uma nota só usado para afinar outros instrumentos musicais).
O código anterior mostra a forma de onda a seguir:
Observe que agora, como a frequência escolhida é mais alta (uma onda 220 vezes mais rápida que a anterior), a duração total de 10 períodos do sinal é muito menor (veja que o valor do eixo horizontal acaba em poucos milisegundos). Se você alterar a duração do sinal para um valor fixo de meio segundo:
duracao_do_sinal=0.5
e reproduzir novamente, você deve ver a seguinte forma de onda:
ou seja, 220 períodos do sinal (como é uma frequência de 440 Hz, em 1s serão 440 períodos, mas aí não conseguiremos nem perceber nada no gráfico sem um “zoom” num intervalo menor de tempo).
Um pequeno passo para a humanidade, um grande passo para o(a) dev
Finalmente chegou a hora de exportar a nossa pequena onda!
Vou postar o último trecho do código adaptado, agora importando o módulo wavefile da biblioteca SciPy. Vamos usar a função write para exportar a nossa nota de 440 Hz em um arquivo WAV sem compressão, e com duração total de 3 segundos.
Caso não consiga executar o código você mesmo, verifique o resultado do arquivo de saída “output.wav” no arquivo que eu subi no Soundcloud:
Legal, posso afinar meu violão usando Python! E agora, o que mais vem pela frente?
Esse é o final da primeira parte dessa publicação! Para facilitar a leitura, tentei escrever algo curto e bem objetivo. Você viu como usar o numpy para gerar ondas de áudio, aprendeu um pouco sobre Nyquist e frequência de amostragem e ainda gerou um arquivo de áudio com a sua nota favorita!
Se você gostou, se inscreva para receber notificação de novas publicações. Dentre outros assuntos que eu quero trazer aqui, estará também a continuação desse projeto, cujos próximos passos envolvem tornar essa nota gerada em um som mais natural (próximo ao som de um instrumento musical), entender mais sobre Fourier e frequências harmônicas e, é claro, melhorar o estilo do nosso código e dos nossos gráficos!
No futuro também pretendo gerar conteúdo em vídeo e em podcast, relacionando com as publicações do blog — assim que sair algo diferente, eu anuncio aqui também. Portanto, se inscreva para acompanhar mais!
Deixe seu comentário caso você tenha gostado do post e escreva sugestões de assuntos de processamento de sinais ou ciência de dados que você gostaria de ver neste blog. :)