Hej! Min første serie artikler vil være rettet mod at studere metoder til kompression og opbevaring af billeder/lyd, såsom JPEG (billede) og WAVE (lyd), og der vil også være eksempler på programmer, der anvender disse formater (.jpg, .wav) i praksis. I denne del vil vi specifikt se på WAVE.
Historik
WAVE (Waveform Audio File Format) er et containerformat til lagring af optagelsen af et lydstream. Denne container bruges typisk til lagring af ukomprimeret lyd i impulsmoduleret kodning. (Citeret fra Wikipedia)
Det blev opfundet og offentliggjort i 1991 sammen med RIFF af Microsoft og IBM (de førende IT-virksomheder på det tidspunkt).
Strukturen af filen
Filen har en headerdel, selve dataene, men ingen footer. Headeren vejer i alt 44 byte.
I headeren findes indstillinger for antallet af bits pr. sample, prøvefrekvens, lyddybde og andre oplysninger, der er nødvendige for lydkortet. (Alle numeriske værdier i tabellen skal skrives i Little-Endian format)
Block name
Block size (B)
Beskrivelse/ Formål
Værdi (hos nogle er den fast)
chunkId
4
Definition af filen som mediecontainer
0x52494646 i Big-Endian ("RIFF")
chunkSize
4
Størrelsen af hele filen uden chunkId og chunkSize
FILE_SIZE — 8
format
4
Definition af typen fra RIFF
0x57415645 i Big-Endian ("WAVE")
subchunk1Id
4
For at filen kan optage mere plads, fortsætter formatet
0x666d7420 i Big-Endian ("fmt ")
subchunk1Size
4
Den resterende header (i byte)
16 som standard (for tilfælde uden kompression af lydstrømmen)
audioFormat
2
Lydformat (afhænger af kompressionsmetoden og strukturen af lyddata)
1 (for PCM, som vi ser på)
numChannels
2
Antal kanaler
1/2, vi tager 1 kanal (3/4/5/6/7… — specifik lydkanal, f.eks. 4 for quad-lyd osv.)
sampleRate
4
Lyden samplingfrekvens (i Hertz)
Jo højere, jo bedre vil lyden være, men jo mere plads kræves der til at skabe en lydspor af samme længde, den anbefalede værdi er 48000 (den mest acceptable lydkvalitet)
byteRate
4
Antal byte pr. sekund
sampleRate numChannels bitsPerSample (fremover)
blockAlign
2
Antal byte for 1 sample
numChannels * bitsPerSample: 8
bitsPerSample
2
Antal bits pr. sample (dybde)
Enhver værdi, der er et multiplum af 8. Jo højere, jo bedre og tungere bliver lyden, fra 32 bits er forskellen ikke mærkbar for mennesker
subchunk2Id
4
Markering af startdata (da der kan være andre elementer i headeren afhængigt af audioFormat)
0x64617461 i Big-Endian ("data")
subchunk2Size
4
Størrelsen på dataområdet
dataens størrelse i int
data
byteRate * varighed af lyd
Lyddata
?
Eksempel med WAVE
Den tidligere tabel kan nemt oversættes til en struktur i C, men vores sprog i dag er Python. Det nemmeste, man kan gøre, ved hjælp af 'bølgen' - støjgeneratoren. For denne opgave har vi ikke brug for høj byteRate og kompression.
Vi starter med at importere de nødvendige moduler:
# 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)Derefter skal vi oprette alle nødvendige variabler fra tabellen baseret på deres størrelser. De variable her afhænger kun af numSamples (antal samples). Jo flere der er, desto længere vil vores støj vare.
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-kanaler er tilstrækkeligt (stereo)
sampleRate = pack('<L', 1000) # 1000 er nok, men hvis du sætter højere, så bliver støjen tydeligere. Ved 1000 lyder det som vind
bitsPerSample = b'x20x00' # 0d32
byteRate = pack('<L', 1000 * 2 * 4) # sampleRate * numChannels * bitsPerSample / 8 (32 bit lyd)
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) # selve støjenVi skal blot skrive dem i den nødvendige rækkefølge (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) # skriverOg sådan er det klart. For at bruge scriptet skal vi tilføje de nødvendige kommandolinjeargumenter:
python3 WAV.py [antal af samples] [output]
antal af samples — antal samples
output — stien til output-filen
Her er et link til en test-lydfil med støj, men for at spare plads har jeg reduceret BPS til 1b/s og antallet af kanaler til 1 (med en 32 bit ukomprimeret stereo lydstrøm på 64kbs blev det 80M ren .wav fil, så kun 10):
Hele koden i sin helhed (WAV.py) (Koden har mange duplikerede værdier af variabler, det er kun et udkast):
from struct import pack # oversættelse af py-objekter til basistyper fra C
from os import urandom # funktion til at læse /dev/urandom, til Windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)]) # lambda for Windows, da urandom ikke findes i Windows
from sys import argv, exit # argumenter til programmet og exit
if len(argv) != 3: # +1 script navn (-1, hvis du vil fryse det)
print('Brug: python3 WAV.py [antal prøver] [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 kanaler er tilstrækkeligt (stereo)
sampleRate = pack('<L', 1000) # 1000 er tilstrækkeligt, men kan være mere.
bitsPerSample = b'x20x00' # 0d32
byteRate = pack('<L', 1000 * 2 * 4) # sampleRate * numChannels * bitsPerSample / 8 (32 bit lyd)
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) # selve støjen
with open(output_path, 'wb') as fh:
fh.write(chunkId + chunkSize + Format + subchunk1ID +
subchunk1Size + audioFormat + numChannels +
sampleRate + byteRate + blockAlign + bitsPerSample +
subchunk2ID + subchunk2Size + data) # skriver resultatet til filenResultat
Nu har du lært lidt mere om digital lyd og hvordan det opbevares. I dette indlæg har vi ikke brugt komprimering (audioFormat), men for at dække hver af de populære, kræves der 10 artikler. Jeg håber, du har lært noget nyt og at det vil hjælpe dig i fremtidige projekter.
Tak!
Kilder
Kilde: habr.com
