Hej! Min första serie artiklar kommer att fokusera på metoder för kompression och lagring av bilder/ljud, såsom JPEG (bilder) och WAVE (ljud), och det kommer också att finnas exempel på program som använder dessa format (.jpg, .wav) i praktiken. I denna del kommer vi att titta särskilt på WAVE.
Historien
WAVE (Waveform Audio File Format) är ett filformat för att lagra inspelningar av ljudströmmen. Denna behållare används vanligtvis för lagring av okomprimerat ljud i pulskodmodulering. (Källa: Wikipedia)
Det skapades och publicerades 1991 tillsammans med RIFF av företagen Microsoft och IBM (de ledande IT-företagen vid den tiden).
Strukturen på filen
Filen har en rubrikdel, själva data, men ingen fotnot. Rubriken väger totalt 44 byte.
I rubriken finns inställningar för antal bitar per prov, avkänningens frekvens, ljuddjup och liknande information som behövs för ljudkortet. (Alla numeriska värden i tabellen ska skrivas i Little-Endian-ordning)
Blocknamn
Blockstorlek (B)
Beskrivning/Ändamål
Värde (vissa av dem är fasta)
chunkId
4
Definition av filen som en media-container
0x52494646 i Big-Endian ("RIFF")
chunkSize
4
Storleken på hela filen utan chunkId och chunkSize
FILE_SIZE — 8
format
4
Definition av typ från RIFF
0x57415645 i Big-Endian ("WAVE")
subchunk1Id
4
För att filen ska ta mer utrymme, fortsättningen på formatet
0x666d7420 i Big-Endian ("fmt ")
subchunk1Size
4
Återstående rubrik (i byte)
16 som standard (för fallet utan komprimering av ljudströmmen)
audioFormat
2
Ljudformat (beroende på komprimeringsmetod och struktur på ljuddata)
1 (för PCM, vilket vi behandlar)
numChannels
2
Antal kanaler
1/2, vi tar 1 kanal (3/4/5/6/7… — specifik ljudspår, till exempel 4 för quad-ljud etc.)
sampleRate
4
Samplingsfrekvens för ljudet (i Hertz)
Ju högre, desto bättre kvalitet på ljudet, men desto mer minne behövs för att skapa en ljudspår av samma längd, det rekommenderade värdet är 48000 (den mest acceptabla ljudkvaliteten)
byteRate
4
Antal byte per sekund
sampleRate numChannels bitsPerSample (nästa)
blockAlign
2
Antal byte för 1 prov
numChannels * bitsPerSample: 8
bitsPerSample
2
Antal bitar per prov (djup)
Valfritt tal som är en multipel av 8. Ju högre, desto bättre och tyngre blir ljudet, från 32 bitar finns det ingen skillnad för människor.
subchunk2Id
4
Märke för början av data (eftersom det kan finnas andra element i rubriken beroende på audioFormat)
0x64617461 i Big-Endian ("data")
subchunk2Size
4
Storlek på dataområdet
storleken på data i int
data
byteRate * längd av ljudet
Ljuddata
?
Exempel med WAVE
Den tidigare tabellen kan enkelt översättas till en struktur i C, men vårt språk idag är Python. Det enklaste vi kan göra med "vågen" är en brusgenerator. För denna uppgift behöver vi varken hög byteRate eller komprimering.
Först importerar vi 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 baserat på deras storlekar. De variabler som inte är konstanta beror här enbart på numSamples (antalet samples). Ju fler samples det finns, desto längre tid kommer vårt brus att pågå.
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 kommer att vara tillräckligt (stereo)
sampleRate = pack('<L', 1000) # 1000 är tillräckligt, men om du sätter högre så kommer brus att höras bättre. Vid 1000 låter det som vind
bitsPerSample = b'x20x00' # 0d32
byteRate = pack('<L', 1000 * 2 * 4) # sampleRate * numChannels * bitsPerSample / 8 (32-bit ljud)
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) # själva brusetDet återstår bara att skriva dem i den nödvändiga ordningen (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) # skriverOch så är det klart. För att använda skriptet, behöver vi lägga till nödvändiga kommandoradsargument:
python3 WAV.py [antal samples] [utdata]
antal samples — kol. samples
utdata — vägen till utdatafilen
Här är en länk till en testljudfil med brus, men för att spara utrymme har jag sänkt BPS till 1b/s och minskat antal kanaler till 1 (med 32-bit okomprimerad stereo ljudström på 64kbs blev det 80M ren .wav fil, men så här är det bara 10):
Hela koden (WAV.py) (Koden har många dupliceringar av värden, det här är bara en skiss):
from struct import pack # översättning av py-objekt till grundläggande typer från C
from os import urandom # funktion för att läsa /dev/urandom, för windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)]) # lambda för windows, eftersom urandom inte finns i windows
from sys import argv, exit # argument till programmet och utgång
if len(argv) != 3: # +1 skriptets namn (-1 om du fryser det)
print('Användning: python3 WAV.py [antal prover] [utdata]')
exit(1)
numSamples = int(argv[1])
outpath = argv[2]
chunkId = b'RIFF'
Format = b'WAVE'
subchunk1ID = b'fmt '
subchunk1Size = b'x10x00x00x00' # 0d16
audioFormat = b'x01x00'
numChannels = b'x02x00' # 2 kanaler är tillräckligt (stereo)
sampleRate = pack('<L', 1000) # 1000 räcker, men kan vara mer.
bitsPerSample = b'x20x00' # 0d32
byteRate = pack('<L', 1000 * 2 * 4) # sampleRate * numChannels * bitsPerSample / 8 (32 bit ljud)
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) # själva ljudet
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 till filResultat
Nu har du lärt dig lite mer om digitalt ljud och hur det lagras. I det här inlägget använde vi ingen komprimering (audioFormat), men det skulle krävas 10 artiklar för att täcka varje av de populära. Jag hoppas att du har lärt dig något nytt som kommer att hjälpa dig i framtida projekt.
Tack!
Källor
Källa: habr.com
