以 WAVE 和 JPEG 格式壓縮/儲存媒體資料的方法,第 1 部分

你好! 我的第一個系列文章將重點研究圖像/音訊壓縮和儲存方法,例如 JPEG(圖像)和 WAVE(聲音),並且還將包括在實踐中使用這些格式(.jpg、.wav)的程式範例。 在這一部分中,我們將看看 WAVE。

故事

WAVE(波形音訊檔案格式)是一種用於儲存音訊串流錄音的容器檔案格式。 此容器通常用於儲存未壓縮的脈衝編碼調變音訊。 (取自維基百科)

它是由微軟和IBM(當時領先的IT公司)於1991年與RIFF一起發明並發布的。

文件結構

該文件有一個頁首部分、資料本身,但沒有頁尾。 標頭總共有 44 個位元組。
標頭包含樣本位數、取樣率、聲音深度等的設定。 聲卡所需的資訊。 (所有數值表值必須以Little-Endian順序寫入)

塊名稱
塊尺寸(B)
描述/目的
值(對某些來說它是固定的

區塊ID
4
將檔案定義為媒體容器
0x52494646(大端字節序)(“RIFF”)

區塊大小
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
每 1 個樣本的位數(深度)
任何 8 的倍數的數字。數字越大,音訊越好、越重;從 32 位元開始,對人類來說沒有區別

子塊2Id
4
資料引用標記(因為可能有其他標頭元素,取決於音訊格式)
Big-Endian 中的 0x64617461(「資料」)

子塊2尺寸
4
資料區大小
int 資料大小

數據
位元組率 * 音訊持續時間
音訊數據
?

波範例

上一個表可以很容易地翻譯成 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]
樣本數 - 計數。 樣品
輸出 — 輸出檔案的路徑

這裡是一個帶有噪音的測試音頻檔案的鏈接,但為了節省內存,我將 BPS 降低到 1b/s,並將通道數降低到 1(使用 32kbs 的 64 位未壓縮立體聲音頻流,結果是80M的純.wav文件,而且只有10): https://instaud.io/3Dcy

整個程式碼(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篇文章。我希望你能學到一些新的東西,這對你將來的開發有幫助。
謝謝!

來源

WAV 檔案結構
WAV - 維基百科

來源: www.habr.com

添加評論