Olá! Minha primeira série de artigos se concentrará no estudo de métodos de compressão e armazenamento de imagem/áudio, como JPEG (imagem) e WAVE (som), e também incluirá exemplos de programas que usam esses formatos (.jpg, .wav) na prática. Nesta parte veremos WAVE.
história
WAVE (Waveform Audio File Format) é um formato de arquivo contêiner para armazenar uma gravação de um fluxo de áudio. Este contêiner é normalmente usado para armazenar áudio modulado por código de pulso não compactado. (Retirado da Wikipédia)
Foi inventado e publicado em 1991 junto com o RIFF pela Microsoft e IBM (empresas líderes de TI da época).
Estrutura de arquivo
O arquivo possui uma parte de cabeçalho, os dados em si, mas não possui rodapé. O cabeçalho pesa um total de 44 bytes.
O cabeçalho contém configurações para o número de bits na amostra, taxa de amostragem, profundidade do som, etc. informações necessárias para a placa de som. (Todos os valores da tabela numérica devem ser escritos na ordem Little-Endian)
Nome do bloco
Tamanho do bloco (B)
Descrição/Objetivo
Valor (para alguns é fixo
pedaçoId
4
Definindo um arquivo como um contêiner de mídia
0x52494646 em Big-Endian (“RIFF”)
tamanho do pedaço
4
Tamanho do arquivo inteiro sem chunkId e chunkSize
FILE_SIZE - 8
formato
4
Definição de tipo do RIFF
0x57415645 em Big-Endian (“WAVE”)
subchunk1Id
4
Para que o arquivo ocupe mais espaço como continuação do formato
0x666d7420 em Big-Endian (“fmt”)
subchunk1Size
4
Cabeçalho restante (em bytes)
16 por padrão (para o caso sem compressão de fluxo de áudio)
formato de áudio
2
Formato de áudio (depende do método de compactação e da estrutura de dados de áudio)
1 (para PCM, que é o que estamos considerando)
numCanais
2
Número de canais
1/2, pegaremos 1 canal (3/4/5/6/7... - uma faixa de áudio específica, por exemplo 4 para som quádruplo, etc.)
taxa de amostragem
4
Taxa de amostragem de áudio (em Hertz)
Quanto mais alto, melhor será o som, mas mais memória será necessária para criar uma trilha de áudio da mesma duração, o valor recomendado é 48000 (a qualidade de som mais aceitável)
taxa de bytes
4
Número de bytes por segundo
taxa de amostragem numCanais bitsPerSample (mais)
bloquearAlign
2
Número de bytes para 1 amostra
numChannels * bitsPerSample: 8
bitsPerSample
2
Número de bits por 1 amostra (profundidade)
Qualquer número que seja múltiplo de 8. Quanto maior o número, melhor e mais pesado será o áudio, a partir de 32 bits não há diferença para humanos
subchunk2Id
4
Marca de referência de dados (já que pode haver outros elementos de cabeçalho dependendo do audioFormat)
0x64617461 em Big-Endian("dados")
subchunk2Size
4
Tamanho da área de dados
tamanho dos dados em int
dados,
byteRate * duração do áudio
Dados de áudio
?
Exemplo de onda
A tabela anterior pode ser facilmente traduzida para uma estrutura em C, mas nossa linguagem de hoje é Python. A coisa mais fácil que você pode fazer é usar uma “onda” - um gerador de ruído. Para esta tarefa não precisamos de byteRate e compactação altos.
Primeiro, vamos importar os módulos necessários:
# WAV.py
from struct import pack # перевод py-объектов в базовые типы из C
from os import urandom # функция для чтения /dev/urandom, для windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)]) # лямбда под windows, т.к. urandom'а в винде нет
from sys import argv, exit # аргументы к проге и выход
if len(argv) != 3: # +1 имя скрипта (-1, если будете замораживать)
print('Usage: python3 WAV.py [num of samples] [output]')
exit(1)
A seguir, precisamos criar todas as variáveis necessárias da tabela de acordo com seus tamanhos. Os valores das variáveis nele dependem apenas de numSamples (número de amostras). Quanto mais deles houver, mais tempo nosso barulho durará.
numSamples = int(argv[1])
output_path = argv[2]
chunkId = b'RIFF'
Format = b'WAVE'
subchunk1ID = b'fmt '
subchunk1Size = b'x10x00x00x00' # 0d16
audioFormat = b'x01x00'
numChannels = b'x02x00' # 2-х каналов будет достаточно (стерео)
sampleRate = pack('<L', 1000) # 1000 хватит, но если поставить больше, то шум будет слышен лучше. С 1000-ю он звучит, как ветер
bitsPerSample = b'x20x00' # 0d32
byteRate = pack('<L', 1000 * 2 * 4) # sampleRate * numChannels * bitsPerSample / 8 (32 bit sound)
blockAlign = b'x08x00' # numChannels * BPS / 8
subchunk2ID = b'data'
subchunk2Size = pack('<L', numSamples * 2 * 4) # * numChannels * BPS / 8
chunkSize = pack('<L', 36 + numSamples * 2 * 4) # 36 + subchunk2Size
data = urandom(1000 * 2 * 4 * numSamples) # сам шум
Resta anotá-los na sequência necessária (como na tabela):
with open(output_path, 'wb') as fh:
fh.write(chunkId + chunkSize + Format + subchunk1ID +
subchunk1Size + audioFormat + numChannels +
sampleRate + byteRate + blockAlign + bitsPerSample +
subchunk2ID + subchunk2Size + data) # записываем
E então, pronto. Para usar o script, precisamos adicionar os argumentos de linha de comando necessários:
python3 WAV.py [num of samples] [output]
número de amostras - contagem. amostras
saída – caminho para o arquivo de saída
Aqui está um link para um arquivo de áudio de teste com ruído, mas para economizar memória diminuí o BPS para 1b/s e diminuí o número de canais para 1 (com um fluxo de áudio estéreo não compactado de 32 bits a 64kbs, acabou sendo 80M de arquivo .wav puro e apenas 10):
O código inteiro (WAV.py) (o código tem muitos valores de variáveis duplicados, isso é apenas um esboço):
from struct import pack # перевод py-объектов в базовые типы из C
from os import urandom # функция для чтения /dev/urandom, для windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)]) # лямбда под windows, т.к. urandom'а в винде нет
from sys import argv, exit # аргументы к проге и выход
if len(argv) != 3: # +1 имя скрипта (-1, если будете замораживать)
print('Usage: python3 WAV.py [num of samples] [output]')
exit(1)
numSamples = int(argv[1])
output_path = argv[2]
chunkId = b'RIFF'
Format = b'WAVE'
subchunk1ID = b'fmt '
subchunk1Size = b'x10x00x00x00' # 0d16
audioFormat = b'x01x00'
numChannels = b'x02x00' # 2-х каналов будет достаточно (стерео)
sampleRate = pack('<L', 1000) # 1000 хватит, но можно и больше.
bitsPerSample = b'x20x00' # 0d32
byteRate = pack('<L', 1000 * 2 * 4) # sampleRate * numChannels * bitsPerSample / 8 (32 bit sound)
blockAlign = b'x08x00' # numChannels * BPS / 8
subchunk2ID = b'data'
subchunk2Size = pack('<L', numSamples * 2 * 4) # * numChannels * BPS / 8
chunkSize = pack('<L', 36 + numSamples * 2 * 4) # 36 + subchunk2Size
data = urandom(1000 * 2 * 4 * numSamples) # сам шум
with open(output_path, 'wb') as fh:
fh.write(chunkId + chunkSize + Format + subchunk1ID +
subchunk1Size + audioFormat + numChannels +
sampleRate + byteRate + blockAlign + bitsPerSample +
subchunk2ID + subchunk2Size + data) # записываем в файл результат
Total
Então você aprendeu um pouco mais sobre o som digital e como ele é armazenado. Neste post não utilizamos compactação (audioFormat), mas para considerar cada um dos populares serão necessários 10 artigos.Espero que você tenha aprendido algo novo por si mesmo e que isso o ajude em desenvolvimentos futuros.
Obrigado!
fontes
Fonte: habr.com