Ferramentas do usuário

Ferramentas do site


prog2:processamento_de_audio

Diferenças

Aqui você vê as diferenças entre duas revisões dessa página.

Link para esta página de comparações

prog2:processamento_de_audio [2019/01/31 17:36] (atual)
Linha 1: Linha 1:
 +====== Processamento de áudio ======
 +
 +Em um sistema digital, um som é normalmente codificado como um vetor de amostras (//​samples//​),​ onde cada amostra corresponde à amplitude do sinal sonoro em um instante de tempo. As figuras a seguir trazem um exemplos de sinais sonoros codificados dessa forma:
 +
 +Trecho de áudio (duração: 45s)
 +
 +{{audio1.png | Trecho de música (45s)}}
 +
 +Detalhe do trecho de áudio acima (duração: 0,2s):
 +
 +{{audio2.png | Detalhe (0,2s)}}
 +
 +Detalhe do trecho de áudio acima (duração: 0,02s):
 +
 +{{audio3.png | Detalhe do detalhe (0,02s)}}
 +
 +Visão conceitual da amostragem de áudio:
 +
 +{{amostragem.png?​500 |Amostragem de sinal analógico}}
 +
 +Na última figura, podem ser identificados o **sinal analógico**,​ proveniente de um microfone ou outro dispositivo de captura, e as **amostras**,​ que correspondem ao valor do sinal medido em intervalos regulares de tempo. O sinal analógico é então representado de forma aproximada pelo seguinte vetor de amostras:
 +
 +  [0, 10, 15, 9, -5, -9, -10, 0, 10, 14, 15, 5, 0, -5, ... ]
 +
 +O padrão PCM ([[https://​en.wikipedia.org/​wiki/​Pulse-code_modulation|Pulse-Code Modulation]]) é um dos padrões mais simples para representar sinais analógicos em meios digitais. Duas informações caracterizam um sinal codificado em PCM:
 +
 +  * **Taxa de amostragem**:​ é o número de amostras do sinal analógico feitas por segundo. Quanto maior a taxa de amostragem, melhor é a representação digital do som, sobretudo nos sinais de frequência mais elevada (agudos). Por outro lado, taxas de amostragem elevada implicam em arquivos maiores. Valores típicos de taxas de amostragem são 48 kHz em DVDs e 44,1 KHz em CDs de música ([[https://​en.wikipedia.org/​wiki/​Sampling_(signal_processing)|exemplos]]).
 +  * **Resolução**:​ é o número de bits usado para representar cada amostra. Quanto maior a resolução,​ mais precisa é a representação de cada amostra e menor a distorção do sinal. CDs de música usam 16 bits de resolução,​ enquanto sistemas de telefonia costumam usar 8 bits ([[https://​en.wikipedia.org/​wiki/​Audio_bit_depth|exemplos]]).
 +
 +No caso de sons com vários canais (estéreo ou //​surround//​),​ deve existir uma sequência de amostras para canal de som, geralmente todas usando a mesma taxa de amostragem e resolução.
 +
 +===== O formato WAV =====
 +
 +Para facilitar a leitura e escrita dos arquivos de áudio, neste projeto será adotado o [[https://​en.wikipedia.org/​wiki/​WAV|formato WAV]], reconhecido pela maioria dos softwares de geração e reprodução de áudio. **Para este projeto será adotado o padrão WAV com codificação PCM de 16 bits com sinal**. Neste formato, cada amostra é representada por um número inteiro de 16 bits com sinal (ou seja, do tipo ''​int16_t'',​ variando entre -32768 e +32767).
 +
 +Um arquivo WAV típico é organizado nos seguintes //chunks// (pedaços) básicos:
 +
 +^ Chunk ID ^ tamanho (bytes) ^ conteúdo ^
 +| ''​RIFF''​ | 8 | cabeçalho RIFF, indica ser um arquivo de áudio |
 +| ''​fmt ''​ | 24 | informações sobre o formato do áudio |
 +| ''​data''​ | *  | amostras do(s) canal(is) de áudio |
 +
 +<note important>​
 +Em alguns arquivos WAV podem haver outros chunks, como LIST e INFO, que não precisam ser considerados neste projeto.
 +</​note>​
 +
 +Esses //chunks// têm os seguintes campos internos:
 +
 +^ nome ^ tamanho ^ tipo ^ valor ^ significado ^
 +| ChunkID | 4 | ''​char[4]''​ | ''"​RIFF"''​ | constante, identifica o tipo de arquivo |
 +| ChunkSize | 4 | ''​uint32_t''​ | filesize - 8 | tamanho do arquivo em bytes, sem considerar este //chunk// |
 +| Format | 4 | ''​char[4]''​ | ''"​WAVE"''​ | constante, define o formato interno |
 +| SubChunk1ID | 4 | ''​char[4]''​ | ''"​fmt "''​ | constante, cabeçalho do //chunk// |
 +| SubChunk1Size | 4 | ''​uint32_t''​ | 16 (PCM) | tamanho deste //chunk// |
 +| Audio format | 2 | ''​uint16_t''​ | 1 (PCM) | codificação utilizada |
 +| Number of channels | 2 | ''​uint16_t''​ | 1, 2, ...  | número de canais de áudio|
 +| Sample rate | 4 | ''​uint32_t''​ | 44100, etc | Taxa de amostragem (amostras/​seg) |
 +| Byte rate | 4 | ''​uint32_t''​ | varia | taxa de bytes por segundo |
 +| Block align | 2 | ''​uint16_t''​ | varia | número de bytes por amostra (soma todos os canais) |
 +| Bits per sample | 2 | ''​uint16_t''​ | 8, 16, 32, ... | bits por amostra, por canal |
 +| SubChunk2ID | 4 | ''​char[4]''​ | ''"​data"''​ | constante, cabeçalho do //chunk// |
 +| SubChunk2Size | 4 | ''​uint32_t''​ | varia | espaço ocupado pelas amostras, em bytes  |
 +| Audio data | varia | varia | varia | amostras de áudio |
 +
 +<note important>​
 +Observe que os campos de tipo ''​char[4]''​ **não são strings**, pois strings precisam ser terminadas por ''​\0'',​ o que não ocorre aqui.
 +</​note>​
 +
 +As amostras de áudio são armazenadas no //chunk// de dados (''​data''​) do arquivo, em sequência por canal e por tempo. Cada amostra tem um tamanho fixo em bytes, definido pelo campo //bits per sample// do cabeçalho. Considerando um áudio com dois canais (//Left// e //Right//), as amostras estarão dispostas da seguinte forma:
 +
 +  L[0], R[0], L[1], R[1], L[2], R[2], L[3], R[3], ...
 +
 +<note tip>
 +Para ler facilmente o cabeçalho de um arquivo WAV, basta definir um //struct// com os mesmos campos, tamanhos e ordem do cabeçalho descrito acima, e então ler os primeiros bytes do arquivo para dentro desse //struct//, usando a função de leitura ''​fread''​. Uma vez lido o //struct//, basta verificar se os campos constantes têm os valores esperados.
 +</​note>​
 +
 +Documentação adicional sobre o formato WAV:
 +
 +  * [[http://​soundfile.sapp.org/​doc/​WaveFormat/​]]
 +  * [[http://​unusedino.de/​ec64/​technical/​formats/​wav.html]]
 +
 +Alguns exemplos de arquivos de áudio WAV para usar no projeto:
 +
 +  * {{adele.wav|Trecho de música (estéreo)}} (clique em "​salvar como..."​)
 +  * {{numbers.zip|Numbers}} (mono, 22050 KHz)
 +
 +Você pode gerar seus próprios arquivos WAV usando programas de processamento de áudio, como o "​Audacity",​ Sox, etc.
 +
 +===== Filtros =====
 +
 +Um filtro de áudio é um programa simples, que recebe como entrada um arquivo de áudio (que pode vir da entrada padrão //stdin//), realiza algum tipo de [[https://​en.wikipedia.org/​wiki/​Audio_signal_processing|processamento de áudio]] e entrega na saída um arquivo de áudio (que pode ser //​stdout//​). Eventuais mensagens de erro devem ser enviadas para a saída de erro (//​stderr//​).
 +
 +<note important>​
 +Lembre-se que o filtro deve atuar em todos os canais do áudio da entrada.
 +</​note>​
 +
 +==== Informações ====
 +
 +Este programa não é exatamente um filtro, pois produz como saída uma listagem das principais informações do áudio informado como entrada.
 +
 +Forma de chamada:
 +
 +  wavinfo -i input
 +
 +Exemplo de saída:
 +
 +  riff tag       : "​RIFF"​
 +  riff size      : 8061776
 +  wave tag       : "​WAVE"​
 +  form tag       : "fmt "
 +  fmt_size ​      : 16
 +  audio_format ​  : 1
 +  num_channels ​  : 2
 +  sample_rate ​   : 44100
 +  byte_rate ​     : 176400
 +  block_align ​   : 4
 +  bits_per_sample:​ 16
 +  data tag       : "​data"​
 +  data size      : 8061740
 +  samples/​channel:​ 2015435
 +
 +==== Reversão ====
 +
 +Este filtro produz como saída um áudio invertido, ou seja, "de trás para a frente"​ em relação ao áudio de entrada.
 +
 +Forma de chamada:
 +
 +  wavrev -i input -o output
 +
 +==== Ajuste de volume ====
 +
 +O filtro de ajuste de volume permite aumentar ou diminuir o volume de áudio do arquivo, de acordo com um fator de ajuste V (0.0 ≤ V ≤ 10.0, com default em 1.0). Ele consiste basicamente em multiplicar o valor de cada amostra de áudio por V.
 +
 +Forma de chamada (''​-l''​ : //level//):
 +
 +  wavvol -l V -i input -o output
 +
 +Exemplo de entrada e de saída (dobra o volume da entrada):
 +
 +  wavvol -l 2.0 -i musica1.wav -o musica2.wav
 +
 +==== Ajuste automático de volume ====
 +
 +Este filtro permite o ajuste automático de volume do arquivo de áudio. Para tal, é necessário encontrar o valor do maior pico no sinal de áudio (em todos os canais) e usá-lo para calcular um fator de ajuste, de modo que todas as amostras de todos os canais fiquem no intervalo de 16 bits com sinal [-32767 ... +32767].
 +
 +Forma de chamada:
 +
 +  wavautovol -i input -o output
 +
 +==== Eco ====
 +
 +Este filtro produz como saída um áudio com eco. O eco é controlado pelos parâmetros //delay// ( inteiro ≥ 0, default 1000 ms), que define o atraso do eco em milissegundos,​ e ''​level''​ (0.0 ≤ level ≤ 1.0, default 0.5), que define o nível do eco em relação ao sinal original.
 +
 +O efeito de eco pode ser definido por esta equação: sample<​sub>//​t//</​sub>​ = sample<​sub>//​t//</​sub>​ + (level × sample<​sub>//​t-delay//</​sub>​)
 +
 +Forma de chamada (''​-t''​ : //time//, ''​-l''​ : //level//):
 +
 +  wavecho -t delay -l level -i input -o output
 +
 +<note tip>
 +Experimente aplicar este filtro a um arquivo de voz, com um atraso bem pequeno (< 10 ms) =)
 +</​note>​
 +
 +==== Estéreo ampliado ====
 +
 +O filtro de estéreo ampliado permite aumentar a separação de canais em um sinal estéreo, gerando um som mais aberto e envolvente. Este filtro só pode ser aplicado a sinais estéreo (com 2 canais).
 +
 +Sendo R(t) e L(t) as amostras de entrada do canal direito e esquerdo em um instante t, a saída R'(t), L'(t) é calculada da seguinte forma:
 +
 +  diff  = R(t) - L(t)
 +  R'(t) = R(t) + k * diff
 +  L'(t) = L(t) - k * diff
 +
 +onde ''​diff''​ é o sinal de diferença entre os dois canais e ''​k''​ é o fator de ampliação do efeito estéreo (0.0 ≤ k ≤ 10.0, default 1.0). 
 +
 +Forma de chamada (''​-l''​ : //level//):
 +
 +  wavwide -l k -i input -o output
 +
 +==== Concatenação ====
 +
 +O filtro de concatenação recebe como entrada **um ou mais arquivos de áudio** e gera uma saída contendo a concatenação das entradas na sequência indicada.
 +
 +Forma de chamada:
 +
 +  wavcat arq1.wav arq2.wav arq3.wav ... -o output
 +
 +<note important>​
 +A combinação de sinais de áudio com taxas de amostragem diferentes exige um procedimento chamado //​reamostragem//​ ([[https://​ccrma.stanford.edu/​~jos/​resample/​|resampling]]),​ que pode ser complexo. Por isso, restrinja seu programa a arquivos com a mesma taxa de amostragem.
 +</​note>​
 +
 +==== Mistura ====
 +
 +O filtro de mistura (mixagem) recebe como entrada um ou mais arquivos de áudio e gera uma saída contendo a mistura (mixagem) das entradas.
 +
 +Forma de chamada:
 +
 +  wavmix arq1.wav arq2.wav arq3.wav ... -o output
 +
 +<note important>​
 +Ao somar as amostras, cuide para não saturar a saída, o que pode gerar distorção ([[https://​en.wikipedia.org/​wiki/​Clipping_(audio)|clipping]]).
 +</​note>​
 +
 +===== Atividade =====
 +
 +  * Implementar os filtros acima definidos como arquivos e comandos separados. Exemplo: o filtro de volume deve ser implementado em um arquivo ''​wavvol.c''​ que gera um executável ''​wavvol'',​ e assim por diante.
 +  * Os filtros devem aceitar como entrada sons no formato WAV PCM 16 bits com sinal e devem gerar como saída sons nesse mesmo formato.
 +  * As rotinas comuns (leitura/​escrita de arquivos, tratamento da linha de comando, etc) devem ser implementadas em arquivos separados, cujos cabeçalhos são incluídos nos arquivos de implementação dos filtros.
 +  * Sempre que possível, as informações do som necessárias às funções devem ser transferidas como parâmetros (por valor ou por referência,​ dependendo da situação). Minimizar o uso de variáveis globais.
 +  * Use alocação dinâmica de memória para os sons, para poder processar arquivos grandes. Só aloque a memória para as amostras após encontrar o número de amostras.
 +  * Construir um ''​Makefile''​ para o projeto:
 +    * Ao menos os alvos ''​all''​ (default), ''​clean''​ e ''​purge''​.
 +    * CFLAGS = ''​-Wall''​
 +    * Compilar e ligar separadamente (gerar arquivos ''​.o''​ intermediários)
 +  * O que deve ser entregue ao professor:
 +    * arquivos ''​.c''​ e ''​.h''​
 +    * arquivo ''​Makefile''​
 +    * **não enviar** os sons de teste
 +
 +<note tip>
 +Para simplificar a implementação e evitar erros, sugere-o o uso dos tipos inteiros de tamanho fixo (''​int16_t'',​ etc), acessíveis através do arquivo ''​inttypes.h''​. ​
 +</​note>​
 +
 +==== Linha de comando ====
 +
 +  * A opção ''​-i''​ indica o nome do arquivo de entrada; se não for informado, deve-se usar a entrada padrão (//​stdin//​).
 +  * A opção ''​-o''​ indica o nome do arquivo de saída; se não for informado, deve-se usar a saída padrão (//​stdout//​).
 +  * Todas as mensagens de erro devem ser enviadas para a saída de erro (//​stderr//​).
 +
 +Essas opções podem ser usadas em qualquer combinação,​ ou seja:
 +
 +<​code>​
 +// entrada e saída em arquivos
 +wavvol -i inputfile.wav ​ -o outputfile.wav
 +wavvol -o outputfile.wav -i inputfile.wav
 +
 +// entrada em arquivo, saída em stdout, vice-versa ou ambos
 +wavvol -i inputfile.wav ​ > outputfile.wav
 +wavvol -o outputfile.wav < inputfile.wav
 +wavvol ​ < inputfile.wav ​ > outputfile.wav
 +
 +// as opções podem estar em qualquer ordem
 +wavvol -l 0.3 -i inputfile.wav -o outputfile.wav
 +wavvol -i inputfile.wav -l 0.3 -o outputfile.wav
 +wavvol -o outputfile.wav -i inputfile.wav -l 0.3
 +</​code>​
 +
 +<note tip>
 +Para ler e tratar mais facilmente as opções da linha de comando, sugere-se usar funções já prontas para isso, como ''​getopt''​ ou ''​arg_parse''​ ([[https://​www.gnu.org/​software/​libc/​manual/​html_node/​Parsing-Program-Arguments.html#​Parsing-Program-Arguments|link]])
 +</​note>​
 +
 +Como os filtros devem tratar a entrada e saída padrão, é possível combinar filtros usando //pipes// UNIX. Por exemplo, podemos usar pipes para construir o efeito [[https://​en.wikipedia.org/​wiki/​Reverse_echo|Reverse Echo]], muito apreciado por alguns grupos de Rock:
 +
 +  wavrev -i input.wav | wavecho -t 500 -l 0.5 | wavrev -o output.wav
 +
 +Caso a adição de eco provoque //​clipping//,​ pode-se atenuar o sinal antes de processá-lo:​
 +
 +  wavvol -l 0.5 -i input.wav | wavrev | wavecho -t 500 -l 0.5 | wavrev | wavautovol -o output.wav
 +
 +<note tip>
 +Para tocar facilmente os arquivos de áudio, pode-se usar os comandos ''​play'',​ ''​aplay''​ ou ''​paplay''​ no terminal do Linux, ou o //preview// de arquivos no gerenciador de arquivos do ambiente gráfico.
 +</​note>​
 +
 +==== Estrutura do código-fonte ====
 +
 +O código-fonte deve ser estruturado em diversos arquivos ''​.c''​ e ''​.h''​ que contenham as funcionalidades a serem implementadas. A figura abaixo traz uma uma **sugestão de estrutura** para o código-fonte (as setas correspondem a ''​include''​s):​
 +
 +{{estrutura-wav.png?​600 |Estrutura do código-fonte}}
  
prog2/processamento_de_audio.txt · Última modificação: 2019/01/31 17:36 (edição externa)