28 7月 2013

[卸甲] wav 檔的金蟬脫殼

非常喜歡吃海鮮,但又非常懶得「剝殼」!一想到剝完蝦蟹的殼,手上不但會有味道,其中的微量元素又讓人面對漫漫長夜時備感寂寞,更別提那些剝下來的殼又是很難處理的廚餘…

啊~那麼也許去吃什麼 X 鼎活蝦一類的餐廳,就不用擔心廚餘的問題了。是啊…不過一個人去這種地方吃,又吃不到幾種花樣…想想就覺得既麻煩又不划算。於是…常吃到的就只剩下鮭魚、旗魚…等各種重金屬殘留較嚴重的大型魚類了…

這麼懶的一個人,今天卻要來推敲 Praat 這套軟體裡計算聲音長度 (以秒做單位) 的方法…呃…實在是想到就全身發出萬丈懶惰光波啊…

故事是這樣開始的…

我一直認為,在 Praat 裡的聲音長度 (以秒做單位) 很不合理。像下圖裡紅框處的數字…

1.1412925170068027 這個數字,在小數點後有 16 位,那是比飛秒 (fs, 10-15) 還要小的存在!即便語音學已經是語言學裡面最硬派的領域了,但究竟有沒有需要用到比飛秒還小的單位呢? (-_- )"

再怎麼不合理,我如果要寫一個程式來相容 Praat 的輸出,那麼就勢必要有辦法找到這個數字是怎麼算出來的才行。

於是,第一個想法是…先用檔案大小試試看吧…
查了一下,檔案的大小是 100706 個 Bit,很快地心算一下,以取樣率是 44100Hz ,因為左右聲道各佔一個 Bit,而這是單聲道的檔案,所以還要除以 2,這麼一來…



嗯…算不出來…XD

於是再用 Python 取得檔案大小來算一下…
得到了一個也是小數點後 16 位的數字。咦?這樣就算出來了嗎?

比對一下…

嗯…這個數字 1.1417913832199547 和剛才的  1.1412925170068027 大多了呢!

然後我才想到,44100Hz 是訊號的取樣率,但 100706 這個數字裡頭,可不只有訊號而已,還有一些檔案標頭什麼的東西 (如下圖)
這張圖看不懂就算了。反正…重點是,粗略地講,我只需要這裡面皮膚色的區塊,但是檔案大小是還包括了紫色和綠色區塊的。所以…我需要另一個工具來取得「只有皮膚色」區塊的大小,然後再來除以 44100。這時候,Python 的 wave 模組就派上用場啦…
利用 wave 打開檔案,並且用 .getnframes() 取得了皮膚色的區塊大小後,再除以 44100 (因為左右聲道一起佔一個 frame,所以不需要再除以 2),就得到了 1.1412925170068027 這個和 Praat 一致的數字了。

能想到脫去不必要的檔頭資訊,推導出這個奇怪的數字是怎麼來的以後固然很高興,但頭大的感覺就像剝完了 20 隻蝦子和 5 隻螃蟹以後才發現滿手都是那股去不掉的海產腥味一樣…

唉…以後我怎麼跟別人解釋為什麼我的程式會用到比「飛秒」還要小的單位來測量聲音長度呢?只說「因為 Praat 也是這麼做啊!」好像有點不負責任。畢竟,實際上,大概到 300毫秒 (ms) 就是人類的感知極限了啊!之後的那些位元數字,只是增加計算時產生誤差的機會罷了…