[英]Python's SoundFile: soundfile.write and clipping
假設我使用 Python 的soundfile
讀取 WAV 文件,
import soundfile
x, fs = soundfile.read("test.wav")
數組x
在float32
且max(x) = 1, min(x) = -1
。 也就是說: x
中的每個樣本都是一個介於 -1 和 1 之間的 float32 數。我對其進行了一些操作並得到y
。 現在我想將y
保存到 WAV 文件中。 假設y
現在的值大於 1(和/或小於 -1),並且我使用
soundfile.write("processed.wav", y, fs)
SoundFile 如何處理超出的值? 它是否進行裁剪(如果y[t] > 1
需要y[t] = 1
)或歸一化(將整個信號除以max(abs(y))
)或其他什么?
我沒有在文檔中找到答案: https : //pysoundfile.readthedocs.io/en/latest/#soundfile.write
import numpy as np
import soundfile as sf
x = np.array([0,0.5,0.75, 1, 2])
sf.write("x.wav", x, 1)
y, fs = sf.read("x.wav")
print(y)
輸出是:
[0. 0.5 0.75 0.99996948 0.99996948]
所以它似乎確實可以剪裁,但我想確定一下。 我可以控制soundfile.write
如何處理超出的值嗎?
這里要回答的重要問題不僅是soundfile 的作用,還有如何確認行為。
讓我們保持這個整潔的小示例程序,它有一些額外的注釋:
import numpy as np
import soundfile as sf
x = np.array([0,0.5,0.75, 1, 2]) # x.dtype is 'float64'
sf.write("x.wav", x, 1) # a wav at sampling rate 1 Hz
y, fs = sf.read("x.wav")
print(y)
WAV 可以有幾種不同的采樣率和數據格式(或位深度)。 一種可能的奇怪行為是 1 Hz 采樣率。 值得慶幸的是,在這種情況下它沒有影響,但一般來說,避免由奇怪的值引起的潛在問題是一個好主意。 堅持標准采樣率,直到您可以定義行為。
Soundfile 的文檔本身並不是不透明的,但您確實需要對信息進行一些搜索。 對於write()
方法,我們看到
subtype (str, optional) – 請參閱 default_subtype() 了解默認值和 available_subtypes() 了解所有可能的值。
然而,另一個重要的信息實際上是在data
字段下
數據的數據類型不選擇寫入文件的數據類型。 音頻數據將轉換為給定的子類型。 將 int 值寫入浮點文件不會將值縮放為 [-1.0, 1.0)。 如果您將值
np.array([42], dtype='int32')
寫入subtype='FLOAT'
文件,則該文件將包含np.array([42.], dtype='float32')
。
基本上,數據類型不是由樣本數據推斷的,而是會縮放到subtype
。
當我們查看default_subtype
我們發現 WAV 的默認值是 16 位 PCM。
棘手的一點是, soundfile 在使用read
讀取信息時會做什么?
好的做法是使用其他東西來確認行為。 如果第二種讀取數據的方式報告相同的信息,那么賓果游戲,我們已經破解了。 如果沒有,則表明至少有一種方法正在更改數據,因此您必須嘗試第三種方法(依此類推)。
讀取數據並確保其未被更改的一種好方法是僅使用十六進制編輯器進行讀取。
在這一點上讓我們提醒自己,我們從soundfile.read()
輸出是:
[0. 0.5 0.75 0.99996948 0.99996948]
上面的十六進制示例創建了一個文件:
52494646 2E000000 57415645 666D7420 10000000 01000100 01000000 02000000 02001000 64617461 0A000000 00000040 0060FF7F FF7F
我們知道它是 16 位樣本,所以最后 10 個字節是我們感興趣的(每個樣本 2 個字節,總共 5 個樣本)
16 位是有符號的,所以我們有±2^{15}
的擺動,即 32768(書呆子別擔心,我會在一分鍾內搞定)
0000 0040 0060 FF7F FF7F
啊,但那是小端格式。 所以,讓我們把它翻過來讓它更容易看
0000 4000 6000 7FFF 7FFF
每一個輪流
0000
是0
,很好很簡單: [0.0]
4000
是16384
,或32768 * 0.5
: [0.5]
6000
是24576
,或32768 * 0.75
: [0.75]
7FFF
是32767
,正是可以描述的峰值正振幅。 由於幅度被縮放到32767
,這就是讀取數據時出現輕微錯誤的原因: 32767 / 32768
等於0.99996948
(有一點舍入誤差)
讓我們通過將最后兩個樣本翻轉為負來確認該行為。
import numpy as np
import soundfile as sf
x = np.array([0,0.5,0.75, -1, -2]) # x.dtype is 'float64'
sf.write("x.wav", x, 1) # a wav at sampling rate 1 Hz
y, fs = sf.read("x.wav")
print(y)
在大端格式中,我們的十六進制數據現在是
0000 4000 6000 8000 8000
8000
是-32768
作為 16 位有符號整數。
由此我們可以確認我們的數據正在被剪裁(未規范化或包裝)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.