在Python中生成正弦波声音

52 投票
6 回答
85419 浏览
提问于 2025-04-17 07:14

我想在Python中生成一个正弦波声音,并且需要能够控制声音的频率、持续时间和相对音量。这里的“生成”是指我希望声音能立即通过扬声器播放,而不是保存成文件。

有什么简单的方法可以做到这一点吗?

6 个回答

4

在Python中处理声音,有一种比较简单且容易安装的方法,就是使用Pygame这个多媒体库。

我推荐你使用它,因为里面有一个叫做pygame.sndarray的子模块,可以让你操作数据向量中的数字,这些数字可以变成一个高级的声音对象,然后在pygame.mixer模块中播放。

你可以在pygame.org网站上找到关于sndarray模块的文档,里面的信息应该足够你使用这个模块了。

17

ivan-onys 给出了一个很好的回答,但我想补充一点:这个脚本生成的声音会比预期短四倍,因为 Pyaudio 的写入方法需要的是 float32 类型的字符串数据。但是当你把 numpy 数组传给这个方法时,它会把整个数组当作一个整体转换成字符串。因此,你需要自己把 numpy 数组中的数据转换成字节序列,像这样:

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

另外,你还需要修改这一行:

stream.write(samples)
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()

撰写回答