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

¡Hola! Mi primera serie de artículos se centrará en el estudio de métodos de almacenamiento y compresión de imágenes/audio como JPEG (imagen) y WAVE (sonido), y también incluirá ejemplos de programas que utilizan estos formatos (.jpg, .wav) en la práctica. En esta parte veremos WAVE.

historia

WAVE (Formato de archivo de audio de forma de onda) es un formato de archivo contenedor para almacenar una grabación de una secuencia de audio. Este contenedor se utiliza normalmente para almacenar audio modulado por código de pulso sin comprimir. (Tomado de Wikipedia)

Fue inventado y publicado en 1991 junto con RIFF por Microsoft e IBM (empresas de TI líderes en esa época).

Estructura de archivos

El archivo tiene una parte de encabezado, los datos en sí, pero no un pie de página. El encabezado pesa un total de 44 bytes.
El encabezado contiene configuraciones para la cantidad de bits en la muestra, frecuencia de muestreo, profundidad del sonido, etc. información necesaria para la tarjeta de sonido. (Todos los valores de la tabla numérica deben escribirse en orden Little-Endian)

Nombre del bloque
Tamaño del bloque (B)
Descripción/Propósito
Valor (para algunos es fijo

ID de trozo
4
Definir un archivo como contenedor de medios
0x52494646 en big-endian ("RIFF")

tamaño de porción
4
Tamaño de todo el archivo sin chunkId y chunkSize
TAMAÑO_ARCHIVO - 8

formato
4
Definición de tipo de RIFF
0x57415645 en big-endian ("ONDA")

subfragmento1Id
4
Para que el archivo ocupe más espacio como continuación del formato
0x666d7420 en big-endian ("fmt")

subfragmento1Tamaño
4
Encabezado restante (en bytes)
16 por defecto (para el caso sin compresión de flujo de audio)

formato de audio
2
Formato de audio (depende del método de compresión y la estructura de datos de audio)
1 (para PCM, que es lo que estamos considerando)

numCanales
2
Numero de canales
1/2, tomaremos 1 canal (3/4/5/6/7... - una pista de audio específica, por ejemplo 4 para audio cuádruple, etc.)

tasa de muestra
4
Frecuencia de muestreo de audio (en Hertz)
Cuanto más alto, mejor será el sonido, pero se necesitará más memoria para crear una pista de audio de la misma longitud, el valor recomendado es 48000 (la calidad de sonido más aceptable)

tasa de bytes
4
Número de bytes por segundo
tasa de muestra numCanales bitsPerSample (más)

bloquearAlinear
2
Número de bytes para 1 muestra
numCanales * bitsPorMuestra: 8

bits por muestra
2
Número de bits por 1 muestra (profundidad)
Cualquier número que sea múltiplo de 8. Cuanto mayor sea el número, mejor y más pesado será el audio, a partir de 32 bits no hay diferencia para los humanos.

subfragmento2Id
4
Marca de referencia de datos (ya que puede haber otros elementos de encabezado dependiendo del formato de audio)
0x64617461 en Big-Endian ("datos")

subfragmento2Tamaño
4
Tamaño del área de datos
tamaño de datos en int

datos
byteRate * duración del audio
datos de audio
?

Ejemplo de onda

La tabla anterior se puede traducir fácilmente a una estructura en C, pero nuestro lenguaje de hoy es Python. Lo más sencillo que puedes hacer es utilizar una "onda", un generador de ruido. Para esta tarea no necesitamos una alta tasa de bytes ni compresión.
Primero, importemos los 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 las variables necesarias de la tabla según sus tamaños. Los valores de las variables que contiene dependen únicamente de numSamples (número de muestras). Cuantos más haya, más durará nuestro ruido.

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

Solo queda anotarlos en la secuencia requerida (como en la tabla):

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

Y así, listo. Para usar el script, necesitamos agregar los argumentos de línea de comando necesarios:
python3 WAV.py [num of samples] [output]
número de muestras - contar. muestras
salida: ruta al archivo de salida

Aquí hay un enlace a un archivo de audio de prueba con ruido, pero para ahorrar memoria bajé el BPS a 1 b/s y bajé el número de canales a 1 (con una transmisión de audio estéreo sin comprimir de 32 bits a 64 kbs, resultó ser 80 M de archivo .wav puro y solo 10): https://instaud.io/3Dcy

El código completo (WAV.py) (El código tiene muchos valores de variables duplicados, esto es solo un boceto):

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

Entonces has aprendido un poco más sobre el sonido digital y cómo se almacena. En esta publicación no utilizamos compresión (audioFormat), pero se necesitarán 10 artículos para considerar cada uno de los populares. Espero que hayas aprendido algo nuevo por ti mismo y esto te ayude en futuros desarrollos.
Gracias!

fuentes

Estructura de archivos WAV
WAV-Wikipedia

Fuente: habr.com

Añadir un comentario