ByteScope

WAV 檔案格式

Waveform Audio,一種未壓縮(通常是 PCM)的 RIFF 音訊容器。

MIME 類型
audio/wav
副檔名
.wav
分類
音訊

Magic number(檔案簽章)

0x0000000052494646RIFFRIFF container
0x0000000857415645WAVE'WAVE' form type

關於 WAV 格式

WAV 把音訊以原始 PCM 取樣存在 RIFF 容器裡,代價是檔案很大,換來的是無損音質與容易編輯 — 這正是錄音與後製的標準格式。標頭是 'RIFF'、長度,接著格式類型 'WAVE'。前四個位元組之後的所有東西都是小端序,而檔案本身是一份扁平的 chunk 清單:四個位元組的 chunk id、四個位元組的大小,然後是那麼多位元組的酬載,並補齊到偶數長度。這種排版就是為什麼 WAV 寫起來輕而易舉、也輕而易舉地錯得很細微:檔案裡沒有任何東西會去對照另一個東西,所以一個跟現實不符的大小欄位,仍然是一個格式完全正確、卻沒有兩個播放器處理方式一致的 WAV。

檔頭結構,一個位元組一個位元組看

位移長度欄位意義
04RIFF chunk idASCII 的 RIFF 四個字母,52 49 46 46。RIFF 是 Microsoft 和 IBM 的通用 Resource Interchange File Format,所以這根本不是 WAV 的標記 — AVI 和 WebP 開頭就是一模一樣的四個位元組。
44RIFF chunk size後面所有東西的位元組數,小端序 32 位元無號整數,也就是檔案長度減八。一段 176478 位元組的錄音這裡會是 56 B1 02 00,也就是 176470。沒有任何東西會強制它正確,而值寫錯是手工寫出來的 WAV 最常見的單一缺陷。
84WAVE form type57 41 56 45。這才是真正說出「這是音訊」的欄位:AVI 這裡是 'AVI ',WebP 是 'WEBP'。一個檢查完位移 0 的 RIFF 就收手的偵測器,分不出這三者。
124'fmt ' chunk id66 6D 74 20 — 注意尾隨的空格,因為每個 RIFF chunk id 都剛好四個位元組,而 'fmt' 只有三個。這個格式 chunk 是必要的,慣例上排在最前面。
164fmt chunk size純 PCM 是 16,附加了兩位元組的延伸大小時是 18,WAVE_FORMAT_EXTENSIBLE 則是 40。只要不是 16,後面每個 chunk 都會被往後推,這就是為什麼位移 36 不是一個可靠的地標。
202Audio format code1 是未壓縮的 PCM,3 是 32 位元 IEEE 浮點,6 是 A-law,7 是 mu-law,而 FE FF(65534)是 WAVE_FORMAT_EXTENSIBLE — 那種情況下真正的編碼是由格式延伸區裡的一個 GUID 指名的。
222Channel count1 是單聲道、2 是立體聲,更多就是環繞配置。用延伸格式時會有一個聲道遮罩說明每條聲道屬於哪一顆喇叭;純 PCM 的 WAV 什麼都不說,所以聲道順序只是慣例。
244Sample rate每秒的取樣框數,小端序。44100 是 44 AC 00 00,48000 是 80 BB 00 00。這是播放器決定播放速度時唯一會看的欄位。
284Byte rate取樣率乘聲道數乘每取樣位元數再除以八 — 44.1 kHz 立體聲 16 位元就是 176400。它完全可以由周圍那三個欄位推導出來,所以拿來檢查可疑的標頭是免費的一致性檢查。
322Block align每個取樣框的位元組數:聲道數乘每取樣位元數除以八,所以 16 位元立體聲是 4。seek 的算術會用到它,所以值寫錯的話,每一次 seek 都會變成一陣雜訊。
342Bits per sample8、16、24 或 32。8 位元 PCM 是無號的,比它寬的全部是有號的二補數,而這個不連續性專門絆倒從零寫轉檔器的人。
364Next chunk id只有在最精簡的情況下,這裡才是 'data' chunk。編碼器很常先放一個 'LIST' INFO 區塊或一個 'fact' chunk — 某個常見編碼器寫出來的檔案這裡是 4C 49 53 54 — 所以剖析器必須把 chunk 清單走一遍,不能假設位移。
variable4'data' chunk size緊接在 64 61 74 61 這個 chunk id 之後的四個位元組,給出後面有幾個位元組的取樣資料。因為它是無號 32 位元,這就是 WAV 那個四 GB 硬上限的來源。

怎麼跟長得像的格式分開

容易搞混的怎麼分
AVI前八個位元組完全相同:兩者都是 RIFF 容器,位移 4 都是小端序長度。位移 8 的格式類型決定一切 — 音訊是 57 41 56 45,影片是 41 56 49 20('AVI ' 尾隨一個空格)。天真的「開頭是 RIFF」檢查會把影片檔案報成音訊,踩的就是這個坑。
WebPRIFF 家族的第三個成員,位移 8 是 57 45 42 50。所以三種毫無關係的媒體類型共用同樣的前八個位元組,只差一個四位元組的字 — 這也是為什麼任何誠實的 RIFF 偵測器都讀十二個位元組而不是四個。
RF64 / BW64超過四 GB 的錄音沒辦法用 32 位元欄位表達自己的長度,所以廣播用的變體把位移 0 的 id 換成 52 46 36 34('RF64')或 42 57 36 34('BW64'),把位移 4 的大小設成 FF FF FF FF,再加一個裝著真正 64 位元大小的 'ds64' chunk。檔案其餘部分就是一般的 WAVE — 這就是為什麼有些播放器打得開它們,有些卻只看到一個壞掉的標頭。
FLAC兩者都是無損的,大家也一天到晚拿來比較,但位元組上毫無歧義:原生 FLAC 開頭是 66 4C 61 43('fLaC'),後面接一個 34 位元組的 STREAMINFO 區塊。一個被改名成 .wav 的 FLAC 檔案哪裡都打不開,因為沒有 WAV 讀取端會去掃描格式類型。
MP3MP3 根本沒有容器。它的開頭不是一個 'ID3' 標籤,就是直接一個音框同步 — 十一個位元全設,也就是 FF 後面接一個高三位元皆為 1 的位元組。它跟 RIFF 沒有半點相似,而一個開頭是 FF FB 的 .wav,就是一個標錯名字的 MP3。

常見的壞法

  • 錄音程式當掉或被強制關閉。音訊資料已經在磁碟上了,但那兩個大小欄位 — 位移 4 的 RIFF 大小和 data chunk 的大小 — 還停在錄音開始時寫下的值,常常是零或一個預先配置的長度。於是播放器提早停住,或是越過取樣值一路讀進後面的東西。修法是算術題:用真正的檔案長度把兩個欄位重算一次,檔案就播得好好的。
  • 四 GB 上限。兩個大小欄位都是無號 32 位元,所以一段長的多軌或戶外錄音會悄悄繞回去,標頭描述的檔案遠小於磁碟上的那個。RF64、BW64 和 Sony 的 Wave64 存在的唯一目的就是逃出這個限制,而它們沒有一個是單純的 WAV。
  • 位元深度跟取樣值對不上。位移 34 說 16,取樣值卻是用 24 位元寫的,於是每一個取樣框的邊界都是錯的,檔案會以大致正確的長度播出一片有節奏的靜電雜訊。標頭本身是合法的;它只是在描述另一批位元組,不是實際存在的那批。
  • 少了 chunk 補齊位元組。RIFF 要求奇數長度的 chunk 後面要跟一個補齊位元組,而那個位元組不算進 chunk 大小裡。寫入端忘了它,後面每個 chunk 的位移就都少一,於是讀取端落在 chunk 中間,報出像 'ata' 加一個 NUL 這種亂七八糟的 chunk id。
  • 前面被塞了位元組。一頁 HTML 錯誤訊息、一個多出來的 UTF-8 BOM,或是被存在音訊前面的郵件標頭,都會把 RIFF 擠離位移 0。每個取樣值都活著;把 52 49 46 46 之前的東西全部刪掉,檔案就一模一樣地回來了。

這個格式現在還在用嗎

WAV 到現在還是音訊後製的工作格式,而且這件事大概不會改變:它無損、每套 DAW 和每個作業系統都讀得懂,而且排版簡單到從零寫一個出來只要一頁程式碼。它不適合當散佈格式 — 一小時的 CD 品質立體聲大約 600 MB,所以任何要離開錄音室的東西都會先編成 AAC、Opus 或 MP3,而無損散佈這一塊已經被 FLAC 接手,因為它用大約一半的空間裝同樣的取樣值,還附一套像樣的中介資料模型。WAV 同時也還是系統短音效、取樣音色庫、語音語料庫,以及任何微控制器或遊戲引擎必須在沒有解碼器的情況下播出來的東西的預設,因為原始 PCM 根本不吃 CPU。它真正的兩個限制是四 GB 上限,以及沒有標準的中介資料方案 — 而這兩點正是廣播領域改用 RF64 和 BWF 的原因。

在這個站上打開 WAV 檔

這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。

相關格式

容易和 WAV 搞混的格式,可以比對它們的開頭位元組。

格式開頭位元組副檔名
FLAC66 4C 61 43.flac
MP349 44 33.mp3
AVI52 49 46 46.avi
WebP52 49 46 46.webp

常見問題

檔案的 magic number 是什麼?

檔案開頭的前幾個位元組,不管檔名寫什麼,它標明的是真正的格式。PNG 一定以 89 50 4E 47 開頭,ZIP 則是 50 4B 03 04。作業系統、瀏覽器和 file 指令讀的都是這幾個位元組,而不是相信副檔名 — 這就是為什麼把檔案改名並不會改變它實際上是什麼。

我要怎麼自己檢查 magic number?

在 macOS 或 Linux 上,xxd -l 16 檔名 會印出前 16 個位元組;在 Windows 上,PowerShell 的 Format-Hex -Path 檔名 -Count 16。或者把檔案丟進本站的檔案類型辨識工具,它會在本機讀取位元組並告訴你是什麼格式,不會上傳任何東西。

為什麼兩種格式的 magic number 會一樣?

因為其中一種是建立在另一種之上。docx、xlsx、pptx、apk、jar 與 epub 骨子裡都是 ZIP 壓縮檔,所以全都以 ZIP 的簽章開頭;要分辨它們得看裡面的項目。Java class 檔和 macOS 的通用二進位檔都以 CA FE BA BE 開頭,靠緊接其後的欄位區分。

有完全沒有 magic number 的檔案嗎?

有。XML、HTML、JSON、SVG、CSV 這類以文字為基礎的格式沒有固定的位元組簽章,所以改用結構來辨識:開頭的 <?xml 宣告、<svg 元素,或是 { 後面接著帶引號的鍵。這種偵測是合理推測而非確定,因此本參考頁會把這些格式標示為結構偵測。

magic number 相符就代表檔案安全嗎?

不代表。它告訴你的是格式,不是內容是否無害 — 一個貨真價實的 PDF 或 docx 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

手上有檔案可能是 WAV?用檔案類型辨識工具查出來 — 它在本機讀取 magic number,不上傳任何東西。或者瀏覽所有格式。