Metodes multivides datu saspiešanai/glabāšanai WAVE un JPEG formātos, 1. daļa

Sveiki! Mana pirmā rakstu sērija koncentrēsies uz tādu attēlu/audio saspiešanas un uzglabāšanas metožu kā JPEG (attēls) un WAVE (skaņa) izpēti, kā arī iekļausim piemērus programmām, kas izmanto šos formātus (.jpg, .wav) praksē. Šajā daļā mēs apskatīsim WAVE.

Stāsts

WAVE (Waveform Audio File Format) ir konteinera faila formāts audio straumes ieraksta glabāšanai. Šo konteineru parasti izmanto, lai saglabātu nesaspiestu impulsa koda modulētu audio. (Ņemts no Vikipēdijas)

To 1991. gadā kopā ar RIFF izgudroja un publicēja Microsoft un IBM (tā laika vadošie IT uzņēmumi).

Faila struktūra

Failam ir galvenes daļa, paši dati, bet nav kājenes. Galvenes kopējais svars ir 44 baiti.
Galvene satur iestatījumus parauga bitu skaitam, parauga ātrumam, skaņas dziļumam utt. informācija, kas nepieciešama skaņas kartei. (Visas skaitliskās tabulas vērtības jāraksta Little-Endia secībā)

Bloķēt nosaukumu
Bloka izmērs (B)
Apraksts/Mērķis
Vērtība (dažiem tā ir fiksēta

chunkId
4
Faila kā multivides konteinera definēšana
0x52494646 Big-Endian (“RIFF”)

gabala izmērs
4
Visa faila lielums bez chunkId un chunkSize
FILE_SIZE — 8

formāts
4
Tipa definīcija no RIFF
0x57415645 Big-Endian (“WAVE”)

subchunk1ID
4
Lai fails aizņemtu vairāk vietas kā formāta turpinājums
0x666d7420 Big-Endian (“fmt”)

apakšgabals1Izmērs
4
Atlikusī galvene (baitos)
16 pēc noklusējuma (gadījumam bez audio straumes saspiešanas)

audio formāts
2
Audio formāts (atkarīgs no saspiešanas metodes un audio datu struktūras)
1 (PCM, ko mēs apsveram)

numChannels
2
Kanālu skaits
1/2, mēs paņemsim 1 kanālu (3/4/5/6/7... - konkrēts audio celiņš, piemēram, 4 četrkanālam utt.)

parauga likme
4
Audio iztveršanas ātrums (hercos)
Jo augstāka, jo labāka būs skaņa, bet jo vairāk atmiņas būs nepieciešams, lai izveidotu tāda paša garuma audio celiņu, ieteicamā vērtība ir 48000 (vispieņemamākā skaņas kvalitāte)

baitu ātrums
4
Baitu skaits sekundē
parauga likme numChannels bitsPerSample (turpmāk)

blockAlign
2
1 parauga baitu skaits
numChannels * bitsPerSample: 8

bitsPerSample
2
Bitu skaits uz 1 paraugu (dziļums)
Jebkurš skaitlis, kas reizināts ar 8. Jo lielāks skaitlis, jo labāks un smagāks būs audio; no 32 bitiem cilvēkiem nav nekādas atšķirības

subchunk2ID
4
Datu atsauces zīme (jo atkarībā no audioformāta var būt arī citi galvenes elementi)
0x64617461 Big-Endian ("dati")

apakšgabals2Izmērs
4
Datu apgabala lielums
datu lielums int

dati
byteRate * audio ilgums
Audio dati
?

WAVE piemērs

Iepriekšējo tabulu var viegli pārtulkot C struktūrā, taču mūsu valoda šodien ir Python. Vienkāršākais, ko varat darīt, ir izmantot “vilni” – trokšņu ģeneratoru. Šim uzdevumam mums nav nepieciešams augsts baitu ātrums un saspiešana.
Vispirms importēsim nepieciešamos moduļus:

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

Tālāk mums ir jāizveido visi nepieciešamie mainīgie no tabulas atbilstoši to lielumam. Mainīgās vērtības tajā ir atkarīgas tikai no paraugu skaita (paraugu skaita). Jo vairāk viņu būs, jo ilgāk mūsu troksnis turpināsies.

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

Atliek tikai tos pierakstīt vajadzīgajā secībā (kā tabulā):

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

Un tā, gatavs. Lai izmantotu skriptu, mums jāpievieno nepieciešamie komandrindas argumenti:
python3 WAV.py [num of samples] [output]
paraugu skaits - skaits. paraugi
output — ceļš uz izvades failu

Šeit ir saite uz testa audio failu ar troksni, bet atmiņas taupīšanas nolūkos samazināju BPS līdz 1b/s un samazināju kanālu skaitu līdz 1 (ar 32 bitu nesaspiestu stereo audio straumi 64kbs, izrādījās 80 M tīra .wav faila un tikai 10): https://instaud.io/3Dcy

Viss kods (WAV.py) (kodam ir daudz mainīgo vērtību dublikātu, šī ir tikai skice):

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

Kopsavilkums

Tātad jūs uzzinājāt nedaudz vairāk par digitālo skaņu un to, kā tā tiek saglabāta. Šajā ierakstā mēs neizmantojām kompresiju (audioFormat), bet, lai apsvērtu katru no populārākajiem, būs nepieciešami 10 raksti. Ceru, ka jūs uzzinājāt kaut ko jaunu un tas jums palīdzēs turpmākajā attīstībā.
Paldies!

avoti

WAV faila struktūra
WAV — Vikipēdija

Avots: www.habr.com

Pievieno komentāru