Metoder för att komprimera/lagra mediadata i WAVE- och JPEG-format, del 1

Hallå! Min första serie artiklar kommer att fokusera på att studera bild-/ljudkomprimering och lagringsmetoder som JPEG (bild) och WAVE (ljud), och kommer även att innehålla exempel på program som använder dessa format (.jpg, .wav) i praktiken. I den här delen ska vi titta på WAVE.

Story

WAVE (Waveform Audio File Format) är ett containerfilformat för att lagra en inspelning av en ljudström. Denna behållare används vanligtvis för att lagra okomprimerat pulskodmodulerat ljud. (Hämt från Wikipedia)

Den uppfanns och publicerades 1991 tillsammans med RIFF av Microsoft och IBM (den tidens ledande IT-företag).

Filstruktur

Filen har en sidhuvud, själva data, men ingen sidfot. Rubriken väger totalt 44 byte.
Rubriken innehåller inställningar för antal bitar i samplingen, samplingshastighet, ljuddjup etc. information som behövs för ljudkortet. (Alla numeriska tabellvärden måste skrivas i Little-Endian-ordning)

Blocknamn
Blockstorlek (B)
Beskrivning/Syfte
Värde (för vissa är det fast

chunkId
4
Definiera en fil som en mediebehållare
0x52494646 på Big-Endian ("RIFF")

chunkSize
4
Storleken på hela filen utan chunkId och chunkSize
FILE_SIZE - 8

format
4
Typdefinition från RIFF
0x57415645 i Big-Endian ("WAVE")

subchunk1Id
4
Så att filen tar mer plats som en fortsättning på formatet
0x666d7420 i Big-Endian ("fmt")

subchunk1Size
4
Återstående rubrik (i byte)
16 som standard (för fallet utan ljudströmskomprimering)

ljudformat
2
Ljudformat (beror på komprimeringsmetod och ljuddatastruktur)
1 (för PCM, vilket är vad vi överväger)

numChannels
2
Antal kanaler
1/2, tar vi 1 kanal (3/4/5/6/7... - ett specifikt ljudspår, till exempel 4 för quad-ljud, etc.)

samplingshastighet
4
Ljudsamplingsfrekvens (i Hertz)
Ju högre, desto bättre blir ljudet, men ju mer minne som krävs för att skapa ett ljudspår av samma längd, det rekommenderade värdet är 48000 (den mest acceptabla ljudkvaliteten)

byteRate
4
Antal byte per sekund
samplingshastighet numChannels bitsPerSample (ytterligare)

blockAlign
2
Antal byte för 1 prov
numChannels * bitsPerSample: 8

bitsPerSample
2
Antal bitar per 1 sampel (djup)
Alla tal som är en multipel av 8. Ju högre siffra, desto bättre och tyngre blir ljudet, från 32 bitar är det ingen skillnad för människor

subchunk2Id
4
Datareferensmärke (eftersom det kan finnas andra rubrikelement beroende på ljudformatet)
0x64617461 i Big-Endian("data")

subchunk2Size
4
Dataområdets storlek
datastorlek i int

datum
byteRate * ljudlängd
Ljuddata
?

WAVE exempel

Den föregående tabellen kan lätt översättas till en struktur i C, men vårt språk för idag är Python. Det enklaste du kan göra är att använda en "våg" - en brusgenerator. För denna uppgift behöver vi inte hög byteRate och komprimering.
Låt oss först importera de nödvändiga modulerna:

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

Därefter måste vi skapa alla nödvändiga variabler från tabellen enligt deras storlekar. Variabelvärdena i den beror bara på numSamples (antal samples). Ju fler av dem det är, desto längre kommer vårt brus att fortsätta.

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

Allt som återstår är att skriva ner dem i önskad sekvens (som i tabellen):

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

Och så, redo. För att använda skriptet måste vi lägga till de nödvändiga kommandoradsargumenten:
python3 WAV.py [num of samples] [output]
antal prover - antal. prover
output — sökväg till utdatafilen

Här är en länk till en testljudfil med brus, men för att spara minne sänkte jag BPS till 1b/s och sänkte antalet kanaler till 1 (med en 32-bitars okomprimerad stereoljudström på 64kbs visade det sig vara 80 miljoner ren .wav-fil och endast 10): https://instaud.io/3Dcy

Hela koden (WAV.py) (Koden har många dubbletter av variabelvärden, detta är bara en skiss):

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

Totalt

Så du har lärt dig lite mer om digitalt ljud och hur det lagras. I det här inlägget använde vi inte komprimering (audioFormat), men för att överväga var och en av de populäraste kommer det att krävas 10 artiklar. Jag hoppas att du lärde dig något nytt för dig själv och att detta kommer att hjälpa dig i framtida utvecklingar.
Tack!

källor

WAV-filstruktur
WAV - Wikipedia

Källa: will.com

Lägg en kommentar