Methoden voor het comprimeren/opslaan van mediagegevens in WAVE- en JPEG-formaten, deel 1

Hallo! Mijn eerste serie artikelen zal zich richten op het bestuderen van beeld-/audiocompressie en opslagmethoden zoals JPEG (beeld) en WAVE (geluid), en zal ook voorbeelden bevatten van programma's die deze formaten (.jpg, .wav) in de praktijk gebruiken. In dit deel zullen we kijken naar WAVE.

Verhaal

WAVE (Waveform Audio File Format) is een containerbestandsformaat voor het opslaan van een opname van een audiostream. Deze container wordt doorgaans gebruikt voor het opslaan van ongecomprimeerde pulscode-gemoduleerde audio. (Overgenomen van Wikipedia)

Het werd in 1991 samen met RIFF uitgevonden en gepubliceerd door Microsoft en IBM (toonaangevende IT-bedrijven van die tijd).

Bestandsstructuur

Het bestand heeft een headergedeelte, de gegevens zelf, maar geen voettekst. De header weegt in totaal 44 bytes.
De header bevat instellingen voor het aantal bits in de sample, de samplefrequentie, de geluidsdiepte, enz. informatie die nodig is voor de geluidskaart. (Alle numerieke tabelwaarden moeten in Little-Endian-volgorde worden geschreven)

Naam blokkeren
Blokgrootte (B)
Beschrijving/Doel
Waarde (voor sommigen staat het vast

chunkId
4
Een bestand definiëren als een mediacontainer
0x52494646 in Big-Endian (“RIFF”)

formaat van een blokje
4
Grootte van het gehele bestand zonder chunkId en chunkSize
FILE_SIZE - 8

formaat
4
Typedefinitie van RIFF
0x57415645 in Big-Endian (“WAVE”)

subchunk1Id
4
Zodat het bestand meer ruimte in beslag neemt als voortzetting van het formaat
0x666d7420 in Big-Endian (“fmt”)

subchunk1grootte
4
Resterende header (in bytes)
Standaard 16 (voor het geval zonder audiostreamcompressie)

audioFormaat
2
Audioformaat (afhankelijk van de compressiemethode en audiodatastructuur)
1 (voor PCM, dat is wat we overwegen)

aantalkanalen
2
Aantal kanalen
1/2, we nemen 1 kanaal (3/4/5/6/7... - een specifiek audiospoor, bijvoorbeeld 4 voor quad-audio, enz.)

monsterRate
4
Audiobemonsteringsfrequentie (in Hertz)
Hoe hoger, hoe beter het geluid zal zijn, maar hoe meer geheugen er nodig zal zijn om een ​​audiotrack van dezelfde lengte te creëren, de aanbevolen waarde is 48000 (de meest acceptabele geluidskwaliteit)

bytesnelheid
4
Aantal bytes per seconde
monsterRate aantalkanalen bitsPerSample (verder)

blokAlign
2
Aantal bytes voor 1 monster
aantalkanalen * bitsPerSample: 8

bitsPerSample
2
Aantal bits per 1 sample (diepte)
Elk getal dat een veelvoud is van 8. Hoe hoger het getal, hoe beter en zwaarder de audio zal zijn; vanaf 32 bits is er voor mensen geen verschil

subchunk2Id
4
Gegevensreferentiemarkering (aangezien er afhankelijk van het audioformaat andere header-elementen kunnen zijn)
0x64617461 in Big-Endian("gegevens")

subchunk2grootte
4
Grootte van het gegevensgebied
grootte van gegevens in int

gegevens
byteRate * audioduur
Audiogegevens
?

WAVE voorbeeld

De vorige tabel kan eenvoudig worden vertaald naar een structuur in C, maar onze taal voor vandaag is Python. Het eenvoudigste wat je kunt doen is een ‘golf’ gebruiken: een ruisgenerator. Voor deze taak hebben we geen hoge byteRate en compressie nodig.
Laten we eerst de benodigde modules importeren:

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

Vervolgens moeten we alle benodigde variabelen uit de tabel maken op basis van hun grootte. De variabele waarden daarin zijn alleen afhankelijk van numSamples (aantal samples). Hoe meer er zijn, hoe langer ons lawaai zal aanhouden.

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

Het enige dat overblijft is ze in de vereiste volgorde op te schrijven (zoals in de tabel):

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

En dus, klaar. Om het script te gebruiken, moeten we de nodige opdrachtregelargumenten toevoegen:
python3 WAV.py [num of samples] [output]
aantal monsters - tellen. monsters
output — pad naar het uitvoerbestand

Hier is een link naar een testaudiobestand met ruis, maar om geheugen te besparen heb ik de BPS verlaagd naar 1b/s en het aantal kanalen verlaagd naar 1 (bij een 32-bits ongecomprimeerde stereo audiostream van 64kbs bleek dat zo te zijn 80M puur .wav-bestand, en slechts 10): https://instaud.io/3Dcy

De volledige code (WAV.py) (de code heeft veel dubbele variabelewaarden, dit is slechts een schets):

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

Totaal

Je hebt dus iets meer geleerd over digitaal geluid en hoe het wordt opgeslagen. In dit bericht hebben we geen gebruik gemaakt van compressie (audioFormat), maar om elk van de populaire artikelen te overwegen, zijn er 10 artikelen nodig. Ik hoop dat je iets nieuws voor jezelf hebt geleerd en dit zal je helpen bij toekomstige ontwikkelingen.
Dank je wel!

bronnen

WAV-bestandsstructuur
WAV-Wikipedia

Bron: www.habr.com

Voeg een reactie