Há cerca de um ano, trabalhei com arquivos WAV gerados, havia vários milhares deles. Tentei marcá-los, classificá-los em pastas, criar metadados. No processo, ouvi algumas peças e, para minha tristeza, descobri que todas começam com um silêncio bastante longo. Era muito chato, especialmente quando você ouvia uma série de arquivos em sequência e constantemente tropeçava em pausas antes de reproduzir cada um deles. Ótimo, o que significa que você também precisa fazer algo a respeito.
Já havia passado algum tempo procurando soluções para remover o silêncio dos arquivos quando de repente me dei conta: isso é WAV! Os dados em arquivos WAV geralmente são áudio PCM, ou seja, cada valor no arquivo especifica a amplitude do som em algum momento. Conseqüentemente, se realmente houver silêncio completo ali, e não ruído branco, então zeros sólidos devem corresponder a esse silêncio no arquivo, certo?
$ xxd testfile1.wav | head -n 100
00000000: 5249 4646 64b9 0e00 5741 5645 666d 7420 RIFFd...WAVEfmt
00000010: 1000 0000 0100 0200 44ac 0000 10b1 0200 ........D.......
00000020: 0400 1000 6461 7461 40b9 0e00 0000 0000 ....data@.......
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
# ... and a lot more zeros below
E aqui está. Bem, isso significa que é mais fácil do que parecia. Basta ler os arquivos, encontrar o local onde esses zeros terminam e remover o fragmento correspondente.
Como os arquivos WAV são lidos
Primeiro, eu precisava me familiarizar mais com o formato WAV para entender como trabalhar com esses arquivos e gerenciar os dados dentro deles. Eu coletei várias fontes; uma das mais úteis acabou sendo a página antiga de stanford.edu (o site não está mais disponível, mas, felizmente, sobreviveu na Wayback Machine). Havia um diagrama muito claro:
Portanto, a estrutura do arquivo WAV parece ser bastante simples: primeiro, um cabeçalho de 44 bytes e, em seguida, os dados reais. Com essas informações, já foi possível iniciar o código. Foi necessário apenas pular os primeiros 44 bytes, remover a sequência de zeros no início da seção de dados e enviar todo o resto para reprodução em sua forma original. Embora eu não possa deixar de acrescentar isso em outra fonte , encontrei as seguintes informações:
“Alguns programas presumem (e isso é muito ingênuo da parte deles) que o preâmbulo no cabeçalho é sempre exatamente 44 bytes (como declarado na tabela acima) e que o resto do arquivo é apenas dados de áudio. Não é seguro fazer tais suposições. "
Bem, decidi que estava tudo bem: escrevi o programa em C, então não havia necessidade de me preocupar muito com segurança.
O código
O código era descomplicado, em menos de cem linhas. Na verdade, ele percorreu todo o arquivo, byte a byte, exceto os primeiros quarenta e quatro, e contou zeros consecutivos. Assim que encontrasse algo diferente de zero, o programa parava, salvava o índice apropriado e começava a ler o arquivo desde o início. Desta vez, ele ignorou tudo o que precede o índice (sem contar o cabeçalho) e gerou todos os outros bytes da maneira padrão.
Não há necessidade de citar todo o código, mas aqui está a parte que nos interessará:
// index was calculated above to be the index of
// the last consecutive zero byte
FILE *f = fopen(argv[1], "rb");
int ind = 0;
int current_byte;
while ((current_byte = fgetc(f)) != EOF) {
if (ind < 44 || ind >= index) {
fputc(current_byte, stdout);
}
ind += 1;
}
fclose(f);
Tudo é legal, tudo é simples. É hora de testar. Executei o programa em um dos arquivos com uma pausa particularmente longa.
./strip_audio testfile1.wav > testfile1.nosilence.wav
Foi verificado o que xxd produz para testfile1.nosilence.wav. Ótimo, sem zeros à esquerda. Então funcionou. Para ter certeza, abrirei rapidamente o arquivo no meu reprodutor de áudio.
Fonte:
Imediatamente, o ruído de estática mais poderoso que ouvi em minha vida me atingiu nos ouvidos. Quase caí da cadeira e tentei desesperadamente tirar meus fones de ouvido. Lembro que era no meio da noite, e o cachorro veio correndo para verificar o que havia de errado comigo.
Onde é que eu me enganei?
Meus ouvidos ainda zumbiam e me sentei tentando compreender minhas decisões precipitadas.
- Erro número 1: era preciso abaixar o som.
- Erro nº 2: você não deveria estar usando fones de ouvido.
- Erro # 3: unidade não registrada.
Você notou o terceiro erro no código que dei acima? Dica: veja o comentário. Calculei o índice da variável como o índice do último byte representando zeros. Isso significa que, menos 44 bytes do cabeçalho, agora reproduzimos apenas o que segue ou se sobrepõe ao índice. index está no último zero da série, ou seja, incluímos um byte zero extra na seção de dados.
Isso pode ser corrigido da seguinte maneira:
// replaced >= with just >
if (ind < 44 || ind > index) {
fputc(current_byte, stdout);
}
Agora não há zeros extras na saída e, se você reproduzir o arquivo, nada de ruim acontecerá. Eu consertei tudo ... Mas pare.
Em arquivos WAV, temos áudio PCM, e zeros neste tipo de dados de áudio correspondem a um silêncio completo. Portanto, esse byte extra não deveria ser completamente silencioso? Por que estava tão alto e tão estático?
Primeiro, vamos comparar um arquivo de áudio normal com o monstro que criei com o Audacity:
Adivinha onde está o monstro? Sim, este é aquele cuja amplitude está estável quase ao máximo. Por que é que?
Como as amostras de áudio são lidas
Voltei às fontes que havia selecionado e tentei descobrir como um erro de uma unidade poderia levar a tal explosão em amplitude. Eu sabia que em meus arquivos a amostra contém 16 bits e há dois canais (estéreo), então comecei a procurar as informações apropriadas. Aqui está o que eu disse na seção sobre áudio PCM estéreo de 16 bits:
“Cada amostra está contida em um inteiro i, que representa o número mínimo suficiente de bytes para armazenar um determinado tamanho de amostra. O byte menos significativo é colocado primeiro na loja. "
"O número mínimo de bytes suficiente para armazenar um determinado tamanho" - o texto é desnecessariamente confuso. i corresponde ao número de bits contidos na amostra. No nosso caso, são dezesseis. Da mesma forma, se tivermos um determinado valor com comprimento de 16 bits, é claro, ele será armazenado em dois bytes. E então um ponto importante: o menos significativo dos bytes está localizado primeiro no armazenamento. Aqui está.
Dê uma olhada no gráfico que fiz para mostrar o que causou um sinal tão forte:
A parte superior mostra meu arquivo de monstro, no qual deixei acidentalmente um byte extra com zeros. Cada uma das três amostras - s1, s2 e s3 - contém dois bytes, e o segundo é mais significativo. Portanto, ao converter esses pares de bytes em decimais, obtemos uma amplitude muito alta.
Ao mesmo tempo, na parte inferior, você pode ver que, se remover o byte zero, as amostras são lidas como deveriam e os valores no arquivo de áudio estão dentro de limites razoáveis.
Acontece que, se eu tivesse áudio de 8 bits, o byte extra ausente não causaria problemas. Mas era de 16 bits e, como resultado, mudei toda a sequência nas amostras, de modo que o byte menos significativo fosse lido como o mais significativo.
conclusões
- Verifique a onda de som de um arquivo de áudio antes de reproduzi-lo no volume máximo
- ( )
- ,