在Python中生成正弦波声音

52
我需要在Python中生成正弦波音效,而且还需要能够控制频率、持续时间和相对音量。所谓“生成”,是指我想立即通过扬声器播放它,而不是保存到文件中。
什么是最简单的方法?

你的目标平台是什么? - Brian Cain
1
主要是MacOS X - 我应该明确说明,抱歉! - astrofrog
1
这个问题类似于https://dev59.com/eXVC5IYBdhLWcg3wZwNT和https://dev59.com/hXVC5IYBdhLWcg3wixs0。 - Brian Cain
1
正弦波生成 - Brian Cain
6个回答

72

使用numpy的版本:

import time

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 = 5.0  # in seconds, may be float
f = 440.0  # sine frequency, Hz, may be float

# generate samples, note conversion to float32 array
samples = (np.sin(2 * np.pi * np.arange(fs * duration) * f / fs)).astype(np.float32)

# per @yahweh comment explicitly convert to bytes sequence
output_bytes = (volume * samples).tobytes()

# for paFloat32 sample values must be in range [-1.0, 1.0]
stream = p.open(format=pyaudio.paFloat32,
                channels=1,
                rate=fs,
                output=True)

# play. May repeat with different volume values (if done interactively)
start_time = time.time()
stream.write(output_bytes)
print("Played sound for {:.2f} seconds".format(time.time() - start_time))

stream.stop_stream()
stream.close()

p.terminate()

没有使用numpy的版本:

import array
import math
import time

import pyaudio

p = pyaudio.PyAudio()

volume = 0.5  # range [0.0, 1.0]
fs = 44100  # sampling rate, Hz, must be integer
duration = 5.0  # in seconds, may be float
f = 440.0  # sine frequency, Hz, may be float

# generate samples, note conversion to float32 array
num_samples = int(fs * duration)
samples = [volume * math.sin(2 * math.pi * k * f / fs) for k in range(0, num_samples)]

# per @yahweh comment explicitly convert to bytes sequence
output_bytes = array.array('f', samples).tobytes()

# for paFloat32 sample values must be in range [-1.0, 1.0]
stream = p.open(format=pyaudio.paFloat32,
                channels=1,
                rate=fs,
                output=True)

# play. May repeat with different volume values (if done interactively)
start_time = time.time()
stream.write(output_bytes)
print("Played sound for {:.2f} seconds".format(time.time() - start_time))

stream.stop_stream()
stream.close()

p.terminate()

1
我发现@yahweh的贡献是正确的。将其包含在代码示例中会很有帮助。 - chris
1
这在我的Linux系统上不起作用,它在播放所有样本之前就被切断了,例如,使用持续时间2时,它只播放不到一秒钟。我使用回调方法来播放所有样本。完整的示例在这里: https://gist.github.com/FrankBuss/3c2f0d1eaf289ef9f659139b96e7a459 - Frank Buss
听起来对我来说不像正弦波! - user404153
寻找一个不依赖于numpy的版本。 - Gringo Suave
1
结果:SystemError: 必须为 '#' 格式定义 PY_SSIZE_T_CLEAN 宏 - Tomasz Gandor

17

ivan-onys给出了一个很好的答案,但是还有一点需要补充: 该脚本将产生比预期短4倍的声音,因为Pyaudio write方法需要float32类型的字符串数据,但是当你将numpy数组传递给该方法时,它会将整个数组转换为字符串实体,因此你必须自己将numpy数组中的数据转换为字节序列,像这样:

samples = (np.sin(2*np.pi*np.arange(fs*duration)*f/fs)).astype(np.float32).tobytes()

而且你也必须更改这一行:

stream.write(samples)

非常有趣的@yahweh。这解决了我另一个问题。你能告诉我为什么.tobytes()解决了这个问题吗? - mm_

4

在Python中,处理声音的一种比较稳定且易于安装的方式是使用Pygame多媒体库。

我建议使用它 - 有pygame.sndarray子模块,可以允许您操作数据向量中的数字,这些数字将成为高级声音对象,可以在pygame.mixer模块中播放。

pygame.org网站上的文档足以使用sndarray模块。


4

对于 Python 3.5+,最好的方法是安装开发人员推荐的包。

http://people.csail.mit.edu/hubert/pyaudio/

对于 Debian,请执行以下操作:

sudo apt-get install python3-all-dev portaudio19-dev

在尝试安装Pyaudio之前


1
从ivan_onys的脚本产生的信号比预期的短四倍。如果当音量为浮点数时返回了TypeError,请尝试将.tobytes()添加到以下行中。
stream.write((volume*samples).tobytes())

@mm_ float32 = 32位,8位 = 1字节,因此float32 = 4个字节。当样本以float32形式传递给stream.write时,字节数(持续时间)除以4。将样本写回.tobytes()时,可以纠正写入float32时四分之一的样本计数。

0

bregman lab toolbox中,您可以找到一组完全符合您要求的函数。这个Python模块有点小问题,但是您可以根据需要调整代码以获得自己的函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接