Python 产生的正弦波声音嗡嗡或';正方形';而不是正弦

Python 产生的正弦波声音嗡嗡或';正方形';而不是正弦,python,python-3.x,audio,wav,waveform,Python,Python 3.x,Audio,Wav,Waveform,早些时候我问了一个类似的问题,但我把问题弄得比必须的复杂。我正在生成一个100 hz的正弦波,然后使用simpleaudio进行播放。 注意:我在将波形编码为.wav文件时遇到了这个问题。听起来与简单音频完全相同。将频道从2更改为1也会更改声音,但无法解决此问题 要安装简单音频: sudo apt-get install -y python3-dev libasound2-dev python -m pip install simpleaudio 独立代码: import numpy as n

早些时候我问了一个类似的问题,但我把问题弄得比必须的复杂。我正在生成一个100 hz的正弦波,然后使用simpleaudio进行播放。
注意:我在将波形编码为.wav文件时遇到了这个问题。听起来与简单音频完全相同。将频道从2更改为1也会更改声音,但无法解决此问题

要安装简单音频:

sudo apt-get install -y python3-dev libasound2-dev
python -m pip install simpleaudio
独立代码:

import numpy as np
import simpleaudio as sa
import matplotlib.pyplot as plt

def generate_sine_tone(numsamples, sample_time, frequency):
    t = np.arange(numsamples) * sample_time # Time vector
    signal = 8388605*np.sin(2*np.pi * frequency*t)
    return signal

if __name__ == "__main__":
    duration = 1
    samprate = 44100 # Sampling rate
    numsamples = samprate*duration# Sample count
    st = 1.0 / samprate # Sample time
    t = np.arange(numsamples) * st # Time vecto

    nchannels = 2
    sampwidth = 3

    signal = generate_sine_tone(numsamples, st, 100)
    signal2 = np.asarray([ int(x) for x in signal ])

    play_obj = sa.play_buffer(signal2, nchannels, sampwidth, samprate)
    print(signal2)
    plt.figure(0)
    plt.plot(signal2)
    plt.show()
在命令行中运行该命令将生成一个1秒(44100个采样)的正弦波图,即正弦波的100个周期。它也会将声音播放到扬声器中,因此在运行前请将系统声音调低一点

我关于这个问题的其他帖子:

预期声音:
接收到的声音(大约):


这个问题让我非常恼火,我非常感谢能提供的任何帮助。

这里有两个问题

较小的一点是,您正在创建单个阵列并将其播放,就像播放立体声一样。您需要设置
nchannels=1
(或者通过创建一个包含两列的数组来复制所有值)

另一个问题是试图创建24位样本。很少有人有足够好的设备和足够好的耳朵来区分24位和16位音频。使用2的示例宽度使事情变得更容易。如果愿意,您可以生成24位样本,并将其规格化为16位以便播放:
signal*=32767/np.max(np.abs(signal))

此代码有效

import numpy as np
import simpleaudio as sa

def generate_sine_tone(numsamples, sample_time, frequency):
    t = np.arange(numsamples) * sample_time # Time vector
    signal = 32767*np.sin(2*np.pi * frequency*t)
    return signal

duration = 1
samprate = 44100 # Sampling rate  
numsamples = samprate*duration# Sample count
st = 1.0 / samprate # Sample time

nchannels = 1
sampwidth = 2

signal = generate_sine_tone(numsamples, st, 100)
signal2 = signal.astype(np.int16)
#signal2 = np.asarray([ int(x) for x in signal ])

play_obj = sa.play_buffer(signal2, nchannels, sampwidth, samprate)
play_obj.wait_done()

这里有两个问题

较小的一点是,您正在创建单个阵列并将其播放,就像播放立体声一样。您需要设置
nchannels=1
(或者通过创建一个包含两列的数组来复制所有值)

另一个问题是试图创建24位样本。很少有人有足够好的设备和足够好的耳朵来区分24位和16位音频。使用2的示例宽度使事情变得更容易。如果愿意,您可以生成24位样本,并将其规格化为16位以便播放:
signal*=32767/np.max(np.abs(signal))

此代码有效

import numpy as np
import simpleaudio as sa

def generate_sine_tone(numsamples, sample_time, frequency):
    t = np.arange(numsamples) * sample_time # Time vector
    signal = 32767*np.sin(2*np.pi * frequency*t)
    return signal

duration = 1
samprate = 44100 # Sampling rate  
numsamples = samprate*duration# Sample count
st = 1.0 / samprate # Sample time

nchannels = 1
sampwidth = 2

signal = generate_sine_tone(numsamples, st, 100)
signal2 = signal.astype(np.int16)
#signal2 = np.asarray([ int(x) for x in signal ])

play_obj = sa.play_buffer(signal2, nchannels, sampwidth, samprate)
play_obj.wait_done()

simpleudio.play_buffer()
函数不会转换数据。它只接受确切的内存缓冲区(即,它从您提供的对象获得的缓冲区),并将其解释为您声称它包含的内容。在程序中,对缓冲区包含的内容(2*3字节项)的描述与实际包含的内容(1*8字节项)不同。不幸的是,在您的示例程序中,这不会导致错误,因为您提供给它的缓冲区的大小恰好是6的精确倍数,即您声称内存缓冲区项的大小(以字节为单位)。如果您再尝试一个示例,numsamples=44101,您将得到一个错误,因为44101*8不能被6整除:

ValueError: Buffer size (in bytes) is not a multiple of bytes-per-sample and the number of channels.
尝试
打印(signal2.itemsize)
显示的内容。您在调用
simpleudio.play\u buffer()
时声称它不是3*2。如果以下条件仍然正确,则即使您尝试从Numpy获取24位缓冲区,也无法获取:

也许这就是为什么本教程告诉您仅对Numpy缓冲区使用16位数据类型的原因,请参阅

Numpy阵列可以用来存储音频,但有几个关键的 要求。如果要存储立体声音频,阵列必须具有 两列,因为每列包含一个音频数据通道。他们 还必须具有有符号16位整数数据类型和采样振幅 因此,值必须在-32768到32767的范围内

这些“缓冲区”是什么?它们是Python对象之间传递低级原始字节数据的一种方式,以及用C语言编写的库。请参见:或:

如果您想从音频数据中创建24位缓冲区,那么您必须使用其他库或低级的逐字节攻击来创建内存缓冲区,因为Numpy不会为您这样做。但您可能可以使用dtype=numpy.float32来获取每个通道具有4字节采样的32位浮点。Simpleaudio从样本量中检测到这一点,例如,对于Alsa:

这有点像用一辆车的重量来确定它是一辆汽车,一辆摩托车还是一辆自行车。这是可行的,但如果只被问及车辆的重量,而完全不被问及车辆的类型,可能会感到奇怪


所以。要修复程序,请使用
asarray()
dtype
参数将数据转换为所需的缓冲区格式,并在
play\u buffer()
中声明正确的格式。或者从正弦生成中删除比例因子8388605,用您实际需要的任何东西替换它,并将其放置在格式规范附近。

函数
simpleudio.play_buffer()
不会转换您的数据。它只接受确切的内存缓冲区(即,它从您提供的对象获得的缓冲区),并将其解释为您声称它包含的内容。在程序中,对缓冲区包含的内容(2*3字节项)的描述与实际包含的内容(1*8字节项)不同。不幸的是,在您的示例程序中,这不会导致错误,因为您提供给它的缓冲区的大小恰好是6的精确倍数,即您声称内存缓冲区项的大小(以字节为单位)。如果您再尝试一个示例,numsamples=44101,您将得到一个错误,因为44101*8不能被6整除:

ValueError: Buffer size (in bytes) is not a multiple of bytes-per-sample and the number of channels.
尝试
打印(signal2.itemsize)
显示的内容。您在调用
simpleudio.play\u buffer()
时声称它不是3*2。如果以下条件仍然正确,则即使您尝试从Numpy获取24位缓冲区,也无法获取:

也许这就是为什么教程告诉您只需为Numpy b使用16位数据类型