Métodos para comprimir/almacenar datos multimedia en formatos WAVE e JPEG, parte 1

Ola! A miña primeira serie de artigos centrarase no estudo de métodos de almacenamento e compresión de imaxe/audio como JPEG (imaxe) e WAVE (son), e tamén incluirá exemplos de programas que utilizan estes formatos (.jpg, .wav) na práctica. Nesta parte veremos WAVE.

Historia

WAVE (formato de ficheiro de audio de forma de onda) é un formato de ficheiro contenedor para almacenar unha gravación dun fluxo de audio. Este recipiente úsase normalmente para almacenar audio modulado por código de pulso sen comprimir. (Tomado da Wikipedia)

Foi inventado e publicado en 1991 xunto co RIFF por Microsoft e IBM (empresas líderes de TI da época).

Estrutura do ficheiro

O ficheiro ten unha parte de cabeceira, os propios datos, pero sen pé de páxina. A cabeceira pesa un total de 44 bytes.
A cabeceira contén axustes para o número de bits da mostra, a frecuencia de mostraxe, a profundidade do son, etc. información necesaria para a tarxeta de son. (Todos os valores da táboa numérica deben escribirse en orde Little-Endian)

Nome do bloque
Tamaño do bloque (B)
Descrición/Obxecto
Valor (para algúns é fixo

chunkId
4
Definición dun ficheiro como contedor multimedia
0x52494646 en Big-Endian ("RIFF")

chunkSize
4
Tamaño de todo o ficheiro sen chunkId e chunkSize
FILE_SIZE - 8

formato
4
Definición de tipo de RIFF
0x57415645 en Big-Endian ("WAVE")

subchunk1Id
4
Para que o ficheiro ocupe máis espazo continuando co formato
0x666d7420 en Big-Endian ("fmt")

subchunk1Tamaño
4
Cabeceira restante (en bytes)
16 por defecto (no caso sen compresión de fluxo de audio)

formato audio
2
Formato de audio (depende do método de compresión e da estrutura de datos de audio)
1 (para PCM, que é o que estamos considerando)

numChannels
2
Número de canles
1/2, tomaremos 1 canle (3/4/5/6/7... - unha pista de audio específica, por exemplo 4 para audio cuádruple, etc.)

taxa de mostra
4
Frecuencia de mostraxe de audio (en hercios)
Canto máis alto, mellor será o son, pero canto máis memoria se necesitará para crear unha pista de audio da mesma lonxitude, o valor recomendado é 48000 (a calidade de son máis aceptable)

byteRate
4
Número de bytes por segundo
taxa de mostra numChannels bitsPerSample (máis)

blockAlign
2
Número de bytes para 1 mostra
numChannels * bitsPerSample: 8

bits por mostra
2
Número de bits por 1 mostra (profundidade)
Calquera número que sexa múltiplo de 8. Canto maior sexa o número, mellor e máis pesado será o audio, a partir de 32 bits non hai diferenzas para os humanos

subchunk2Id
4
Marca de referencia de datos (xa que pode haber outros elementos de cabeceira dependendo do formato de audio)
0x64617461 en Big-Endian ("datos")

subchunk2Tamaño
4
Tamaño da área de datos
tamaño dos datos en int

datos
byteRate * duración do audio
Datos de audio
?

Exemplo WAVE

A táboa anterior pódese traducir facilmente a unha estrutura en C, pero a nosa linguaxe para hoxe é Python. O máis sinxelo que podes facer é usar unha "onda" - un xerador de ruído. Para esta tarefa non precisamos de alta byteRate e compresión.
En primeiro lugar, imos importar os módulos necesarios:

# 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 continuación, necesitamos crear todas as variables necesarias da táboa segundo os seus tamaños. Os valores das variables nel dependen só de numSamples (número de mostras). Cantos máis haxa, máis tempo durará o noso ruído.

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)  # сам шум

Só queda anotalos na secuencia requirida (como na táboa):

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # записываем

E así, listo. Para usar o script, necesitamos engadir os argumentos da liña de comandos necesarios:
python3 WAV.py [num of samples] [output]
número de mostras - contar. mostras
saída — camiño ao ficheiro de saída

Aquí tes unha ligazón a un ficheiro de son de proba con ruído, pero para aforrar memoria baixei o BPS a 1b/s e baixei o número de canles a 1 (cun ​​fluxo de audio estéreo sen comprimir de 32 bits a 64 kbs, resultou ser 80 M de ficheiro .wav puro, e só 10): https://instaud.io/3Dcy

O código completo (WAV.py) (O código ten moita duplicación de valores variables, isto é só un esbozo):

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

Así que aprendiches un pouco máis sobre o son dixital e como se almacena. Neste post non usamos a compresión (audioFormat), pero para considerar cada un dos populares, serán necesarios artigos 10. Espero que aprendas algo novo por ti mesmo e que che axude en desenvolvementos futuros.
Grazas!

Fontes

Estrutura do ficheiro WAV
WAV - Wikipedia

Fonte: www.habr.com

Engadir un comentario