我在pyaudio中按预期生成频率为440hz的声波,但即使我使用相同的样本数组来保存wav文件,它也不会保存相同的声音,我无法找出原因
以下是代码:
import wave
import numpy as np
import pyaudio
p = pyaudio.PyAudio()
volume = 0.5 # range [0.0, 1.0]
fs = 44100 # sampling rate, Hz, must be integer
duration = 2.0 # in seconds, may be float
f = 440.0 # sine frequency, Hz, may be float
channels = 1
# open stream (2)
stream = p.open(format=pyaudio.paFloat32,
channels=channels,
rate=fs,
output=True)
def get_value(i):
return np.sin(f * np.pi * float(i) / float(fs))
samples = np.array([get_value(a) for a in range(0, fs)]).astype(np.float32)
for i in range(0, int(duration)):
stream.write(samples, fs)
wf = wave.open("test.wav", 'wb')
wf.setnchannels(channels)
wf.setsampwidth(3)
wf.setframerate(fs)
wf.setnframes(int(fs * duration))
wf.writeframes(samples)
wf.close()
# stop stream (4)
stream.stop_stream()
stream.close()
# close PyAudio (5)
p.terminate()
https://gist.github.com/badjano/c727b20429295e2695afdbc601f2334b
答案 0 :(得分:2)
我认为主要问题是您使用float32
模块不支持的wave
数据类型。
您可以使用int16
或int32
,也可以使用24位整数进行手动转换。
由于您使用的是wf.setsampwidth(3)
,我假设您要使用24位数据?
我已经写了little tutorial about the wave
module(包括如何处理24位数据)和overview about different modules for handling sound files。
您可能也对我关于creating a simple signal的教程感兴趣。
由于您已经在使用NumPy,我建议您使用支持NumPy阵列的库,并为您完成所有转换。 我个人的偏好是使用soundfile模块,但我非常偏颇。 对于播放,我还建议使用支持NumPy的库。我的建议是sounddevice模块,但我也非常偏向于此。
如果您想要遵循我的建议,您的代码可能会变成类似的内容(包括处理volume
并在窦性参数中修复2
的缺失因素):
from __future__ import division
import numpy as np
import sounddevice as sd
import soundfile as sf
volume = 0.5 # range [0.0, 1.0]
fs = 44100 # sampling rate, Hz
duration = 2.0 # in seconds
f = 440.0 # sine frequency, Hz
t = np.arange(int(duration * fs)) / fs
samples = volume * np.sin(2 * np.pi * f * t)
sf.write('myfile.wav', samples, fs, subtype='PCM_24')
sd.play(samples, fs)
sd.wait()
<强>更新强>
如果你想继续使用PyAudio,那很好。
但是,您必须手动将浮点数组(值从-1.0
转换为1.0
)到适当范围内的整数,具体取决于您要使用的数据类型。
我上面提到的第一个链接包含文件utility.py,其中有一个函数float2pcm()
来执行此操作。
这是该功能的缩写版本:
def float2pcm(sig, dtype='int16'):
i = np.iinfo(dtype)
abs_max = 2 ** (i.bits - 1)
offset = i.min + abs_max
return (sig * abs_max + offset).clip(i.min, i.max).astype(dtype)