Metody kompresji/przechowywania danych multimedialnych w formatach WAVE i JPEG, część 1

Cześć! Moja pierwsza seria artykułów skupi się na badaniu metod kompresji i przechowywania obrazu/dźwięku, takich jak JPEG (obraz) i WAVE (dźwięk), a także będzie zawierać przykłady programów wykorzystujących te formaty (.jpg, .wav) w praktyce. W tej części przyjrzymy się WAVE.

Historia

WAVE (Waveform Audio File Format) to format pliku kontenera służący do przechowywania nagrania strumienia audio. Kontener ten jest zwykle używany do przechowywania nieskompresowanego sygnału audio modulowanego impulsowo. (Wzięte z Wikipedii)

Został wynaleziony i opublikowany w 1991 roku wraz z RIFF przez Microsoft i IBM (wiodące wówczas firmy informatyczne).

Struktura pliku

Plik zawiera część nagłówkową, czyli same dane, ale nie zawiera stopki. Nagłówek waży łącznie 44 bajty.
Nagłówek zawiera ustawienia dotyczące liczby bitów próbki, częstotliwości próbkowania, głębi dźwięku itp. informacje potrzebne do karty dźwiękowej. (Wszystkie wartości tabeli numerycznej muszą być zapisane w kolejności Little-Endian)

Nazwa bloku
Rozmiar bloku (B)
Opis/Cel
Wartość (dla niektórych jest stała

identyfikator kawałka
4
Definiowanie pliku jako kontenera multimediów
0x52494646 w Big-Endian („RIFF”)

Rozmiar kawałka
4
Rozmiar całego pliku bez chunkId i chunkSize
ROZMIAR_PLIKU - 8

format
4
Definicja typu z RIFF
0x57415645 w Big-Endian („Fala”)

podkawałek1Id
4
Aby plik zajmował więcej miejsca jako kontynuacja formatu
0x666d7420 w Big-Endian („fmt”)

podkawałek 1Rozmiar
4
Pozostały nagłówek (w bajtach)
domyślnie 16 (w przypadku bez kompresji strumienia audio)

Format audio
2
Format audio (w zależności od metody kompresji i struktury danych audio)
1 (dla PCM, który właśnie rozważamy)

liczba kanałów
2
Liczba kanałów
1/2, weźmiemy 1 kanał (3/4/5/6/7... - konkretna ścieżka audio, np. 4 dla quad audio itp.)

próbna stawka
4
Częstotliwość próbkowania audio (w hercach)
Im wyższy, tym lepszy będzie dźwięk, ale im więcej pamięci będzie potrzebne do utworzenia ścieżki audio o tej samej długości, zalecana wartość to 48000 (najbardziej akceptowalna jakość dźwięku)

szybkość bajtu
4
Liczba bajtów na sekundę
próbna stawka liczba kanałów bitsPerSample (dalej)

blokWyrównaj
2
Liczba bajtów na 1 próbkę
liczba kanałów * bity na próbkę: 8

bity na próbkę
2
Liczba bitów na 1 próbkę (głębokość)
Dowolna liczba będąca wielokrotnością 8. Im wyższa liczba, tym lepszy i cięższy będzie dźwięk; od 32 bitów nie ma różnicy dla ludzi

podkawałek2Id
4
Znak odniesienia do danych (ponieważ mogą istnieć inne elementy nagłówka w zależności od formatu audio)
0x64617461 w Big-Endian („dane”)

podkawałek 2Rozmiar
4
Rozmiar obszaru danych
rozmiar danych w calach

dane
byteRate * czas trwania dźwięku
Dane dźwiękowe
?

Przykład FALA

Poprzednią tabelę można łatwo przełożyć na strukturę w C, ale naszym dzisiejszym językiem jest Python. Najłatwiej jest użyć „fali” – generatora szumu. Do tego zadania nie potrzebujemy dużej szybkości bajtów i kompresji.
Najpierw zaimportujmy niezbędne moduły:

# 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)

Następnie musimy utworzyć wszystkie niezbędne zmienne z tabeli zgodnie z ich rozmiarami. Wartości zmiennych w nim zależą tylko od numSamples (liczba próbek). Im będzie ich więcej, tym dłużej będzie trwał nasz hałas.

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

Pozostaje tylko zapisać je w wymaganej kolejności (jak w tabeli):

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

I tak, gotowe. Aby skorzystać ze skryptu, musimy dodać niezbędne argumenty wiersza poleceń:
python3 WAV.py [num of samples] [output]
liczba próbek - liczba. próbki
wyjście — ścieżka do pliku wyjściowego

Oto link do testowego pliku audio z szumem, ale aby zaoszczędzić pamięć, obniżyłem BPS do 1b/s i obniżyłem liczbę kanałów do 1 (przy 32-bitowym nieskompresowanym strumieniu audio stereo przy 64kbs okazało się, że jest to 80M czystego pliku .wav i tylko 10): https://instaud.io/3Dcy

Cały kod (WAV.py) (Kod zawiera wiele zduplikowanych wartości zmiennych, to tylko szkic):

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)  # записываем в файл результат

Łączny

Dowiedziałeś się więc trochę więcej o dźwięku cyfrowym i sposobie jego przechowywania. W tym poście nie zastosowaliśmy kompresji (audioFormat), ale aby rozważyć każdy z popularnych, potrzeba artykułów 10. Mam nadzieję, że nauczyłeś się czegoś nowego dla siebie i pomoże ci to w przyszłych pracach.
Dziękuję!

Źródła informacji

Struktura pliku WAV
WAV – Wikipedia

Źródło: www.habr.com

Dodaj komentarz