Métodos para compactar/armazenar dados de mídia nos formatos WAVE e JPEG, parte 1

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): https://instaud.io/3Dcy

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

Estrutura do arquivo WAV
WAV - Wikipédia

Fonte: habr.com

Adicionar um comentário