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

你好!我的第一系列文章將重點介紹圖像/音訊壓縮和儲存方法,例如 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 個): 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

添加評論