首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python的SoundFile: soundfile.write和裁剪

Python的SoundFile: soundfile.write和裁剪
EN

Stack Overflow用户
提问于 2021-09-30 08:08:46
回答 1查看 5.6K关注 0票数 0

假设我使用Python的soundfile读取一个WAV文件,

代码语言:javascript
复制
import soundfile
x, fs = soundfile.read("test.wav")

数组x位于float32max(x) = 1, min(x) = -1中。也就是说:x中的每个示例都是一个介于-1和1之间的float32数。我对它做了一些操作,得到了y。现在,我想将y保存到WAV文件中。假设y现在的值大于1(和/或小于-1),我使用

代码语言:javascript
复制
soundfile.write("processed.wav", y, fs)

SoundFile如何处理超出的值?它是做剪裁(如果y[t] > 1是采用y[t] = 1)还是规范化(将整个信号除以max(abs(y)))还是其他什么?

我没有在文档中找到答案:https://pysoundfile.readthedocs.io/en/latest/#soundfile.write

代码语言:javascript
复制
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)

产出如下:

代码语言:javascript
复制
[0.         0.5        0.75       0.99996948 0.99996948]

所以看起来确实有剪裁,但我想确定。我能控制soundfile.write处理超值的方式吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-30 11:03:49

这里要回答的重要问题不仅仅是声谱做了什么,而是如何确认这种行为。

让我们保持这个整洁的小示例程序,它有一些额外的注释是:

代码语言:javascript
复制
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可以在采样率和数据格式(或位深度)上有一些不同的味道。奇数行为的一个潜在因素是1Hz采样率。谢天谢地,在这种情况下,它没有影响,但这将是一个好主意,以避免潜在的问题产生奇怪的价值观。坚持一个标准的抽样率,直到你可以确定行为。

Soundfile的文档本身并不是不透明的,但是您确实需要对信息进行一点搜索。对于我们看到的write()方法

子类型(str,可选)-默认值参见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。

最棘手的一点是,当使用read读取信息时,声音文件会做些什么?

好的做法是用其他的东西来证实这种行为。如果第二种读取数据的方法报告了同样的信息,那么我们已经破解了。如果不是,那么这意味着至少有一个方法正在更改数据,因此您必须尝试第三个方法(以此类推)。

读取数据并确保数据未被更改的一个好方法是使用十六进制编辑器读取数据。

现在让我们提醒自己,我们来自soundfile.read()的输出是:

代码语言:javascript
复制
[0.         0.5        0.75       0.99996948 0.99996948]

上面的十六进制示例创建了一个文件:

代码语言:javascript
复制
52494646 2E000000 57415645 666D7420 10000000 01000100 01000000 02000000 02001000 64617461 0A000000 00000040 0060FF7F FF7F

我们知道它是16位样本,所以最后10个字节是我们感兴趣的(每个样本2字节,总共5个样本)。

16位是签名的,所以我们有一个±2^{15}的swing,即32768 (不要担心书呆子,我马上就到)

代码语言:javascript
复制
0000 0040 0060 FF7F FF7F

啊,但那是小endian格式的。所以,让我们把它转过来,让它更容易看到。

代码语言:javascript
复制
0000 4000 6000 7FFF 7FFF

每一个依次

  • 00000,很好,很简单:[0.0]
  • 400016384,或32768 * 0.5[0.5]
  • 600024576,或32768 * 0.75[0.75]
  • 7FFF32767,正好是可以描述的峰值正振幅。

由于振幅被缩放到32767,这就是当读取数据时出现轻微错误的原因:32767 / 32768等于0.99996948 (有一点舍入误差)。

让我们通过翻转最后两个样本的负值来确认这种行为。

代码语言:javascript
复制
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)

在大endian格式中,我们的十六进制数据现在是

代码语言:javascript
复制
0000 4000 6000 8000 8000

8000-32768作为一个16位有符号整数.

由此,我们可以确认我们的数据正在被裁剪(而不是规范化或包装)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69388531

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档