你好!我的第一系列文章將重點介紹圖像/音訊壓縮和儲存方法,例如 JPEG(圖像)和 WAVE(音訊),並且還將包括在實踐中使用這些格式(.jpg、.wav)的程式範例。在本部分中,我們將特別關注 WAVE。
故事
WAVE(波形音訊檔案格式)是一種用於儲存音訊串流錄音的容器檔案格式。此容器通常用於儲存未壓縮的脈衝編碼調變音訊。 (取自維基百科)
它是由微軟和 IBM(當時領先的 IT 公司)於 1991 年發明並與 RIFF 一起發布的。
文件結構
該文件有文件頭、數據本身,但沒有頁腳。標頭總共重 44 個位元組。
標頭包含聲卡所需的樣本位數、取樣頻率、聲音深度等資訊的設定。 (表中的所有數值必須按照 Little-Endian 順序書寫)
區塊名稱
塊大小(B)
描述/目的
價值(對某些人來說是固定的)
區塊ID
4
將檔案定義為媒體容器
大端字節序 (“RIFF”) 中的 0x52494646
區塊大小
4
不包含 chunkId 和 chunkSize 的整個檔案的大小
文件大小 — 8
格式
4
來自 RIFF 的類型定義
大端位元組序 (“WAVE”) 中的 0x57415645
子塊1Id
4
為了使文件佔用更多空間,請繼續格式化
大端字節序中的 0x666d7420(“fmt”)
子塊1大小
4
剩餘標頭(以位元組為單位)
預設為 16(適用於沒有音訊串流壓縮的情況)
音訊格式
2
音訊格式(取決於音訊資料的壓縮方法和結構)
1(對於 PCM,這就是我們正在研究的)
頻道數量
2
通道數
1/2,我們將採用 1 個頻道(3/4/5/6/7...... — 特定音軌,例如 4 個用於四聲道等)
取樣率
4
音頻取樣頻率(赫茲)
該值越高,音質越好,但創建相同長度的音軌所需的內存也越多,建議值為48000(最可接受的音質)
位元組率
4
每秒的位元組數
取樣率 頻道數量 比特/樣本 (更多)
區塊對齊
2
1 個樣本的位元組數
頻道數 * 樣本位數:8
每樣本位數
2
每個樣本的位數(深度)
8 的倍數,越多越好,音訊越重,32 位元對人來說沒有差別
子塊2Id
4
資料起始計數標記(因為可能還有其他標頭元素,取決於 audioFormat)
Big-Endian 中的 0x64617461(「資料」)
子塊2大小
4
資料區大小
int 格式的資料大小
數據
位元組率 * 音訊時長
音訊數據
?
以 WAVE 為例
上表可以很容易地轉換成 C 結構,但我們今天選擇的語言是 Python。使用「波」可以做的最簡單的事情就是製造噪音產生器。對於這個任務,我們不需要高位元組率和壓縮。
首先,讓我們導入必要的模組:
# 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)接下來,我們需要根據大小從表格中建立所有必要的變數。其中非常量值在這裡只取決於numSamples(樣本數)。他們的數量越多,我們的噪音持續的時間就越長。
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) # сам шум剩下的就是按照要求的順序把它們寫下來(如表所示):
with open(output_path, 'wb') as fh:
fh.write(chunkId + chunkSize + Format + subchunk1ID +
subchunk1Size + audioFormat + numChannels +
sampleRate + byteRate + blockAlign + bitsPerSample +
subchunk2ID + subchunk2Size + data) # записываем就這樣,就完成了。要使用該腳本,我們需要新增必要的命令列參數:
python3 WAV.py [num of samples] [output]
樣本數 — col。樣品
輸出——輸出檔案的路徑
下面是一個帶有噪音的測試音頻文件的鏈接,但為了節省內存,我將 BPS 降低到 1b/s,並將通道數降低到 1(使用 32kbs 的 64 位未壓縮立體聲音頻流,我得到了 80M 的干淨 .wav 文件,而這種方式只有 10 個):
整個程式碼(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)
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) # записываем в файл результат總
現在您對數位音訊及其儲存方式有了更多的了解。在這篇文章中,我們沒有使用壓縮(audioFormat),但要考慮每一種流行的壓縮方式,就需要 10 篇文章。我希望你能學到一些新的東西,這將有助於你未來的發展。
謝謝!
來源
來源: www.habr.com
