简单的Pygame频率音频

9 投票
2 回答
15658 浏览
提问于 2025-04-17 04:35

我想知道怎么用Pygame创建一个440赫兹的声音,让它能一直平滑地播放下去。我觉得这应该很简单,但我不想用任何麻烦的音频文件来完成这个任务。我的最终目的是在按住一个键的时候播放这个音符,就像我在另一个问题中提到的那样。如果有人能帮我,我会非常感激,因为我已经花了很多时间在寻找答案上。

2 个回答

10

在遇到太多“ValueError: 数组深度必须与混音器通道数量匹配”的错误后,我找到一个可以正常工作的例子,链接在这里:http://www.mail-archive.com/pygame-users@seul.org/msg16140.html。这个例子能正确生成一个多维的16位整数数组,适用于立体声混音器。下面是一个最小的可运行示例,主要是从之前的链接中提取的,并加上了必要的pygame部分。这个示例在Python 2.7.2和pygame版本'1.9.1release'中测试过。

这个例子会在一个扬声器播放440赫兹的音调,而在另一个扬声器播放550赫兹的音调,适用于立体声设置。在调整播放时长时,我发现如果把“duration”变量设置为非整数,就会在声音循环中出现可听到的咔嗒声。

import pygame
from pygame.locals import *

import math
import numpy

size = (1366, 720)

bits = 16
#the number of channels specified here is NOT 
#the channels talked about here http://www.pygame.org/docs/ref/mixer.html#pygame.mixer.get_num_channels

pygame.mixer.pre_init(44100, -bits, 2)
pygame.init()
_display_surf = pygame.display.set_mode(size, pygame.HWSURFACE | pygame.DOUBLEBUF)


duration = 1.0          # in seconds
#freqency for the left speaker
frequency_l = 440
#frequency for the right speaker
frequency_r = 550

#this sounds totally different coming out of a laptop versus coming out of headphones

sample_rate = 44100

n_samples = int(round(duration*sample_rate))

#setup our numpy array to handle 16 bit ints, which is what we set our mixer to expect with "bits" up above
buf = numpy.zeros((n_samples, 2), dtype = numpy.int16)
max_sample = 2**(bits - 1) - 1

for s in range(n_samples):
    t = float(s)/sample_rate    # time in seconds

    #grab the x-coordinate of the sine wave at a given time, while constraining the sample to what our mixer is set to with "bits"
    buf[s][0] = int(round(max_sample*math.sin(2*math.pi*frequency_l*t)))        # left
    buf[s][1] = int(round(max_sample*0.5*math.sin(2*math.pi*frequency_r*t)))    # right

sound = pygame.sndarray.make_sound(buf)
#play once, then loop forever
sound.play(loops = -1)


#This will keep the sound playing forever, the quit event handling allows the pygame window to close without crashing
_running = True
while _running:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            _running = False
            break

pygame.quit()
6

什么是440赫兹的声音?其实440赫兹的声音有很多种波形,比如正弦波、锯齿波、方波等等。你也可以想象成一个长笛在演奏一个A音,这也算是440赫兹的声音。

如果你想要生成一个正弦波,似乎可以用pygame.sndarray.samples来创建一个声音对象。(我还没测试过这个)你可以用下面的代码来生成样本:

samples = [math.sin(2.0 * math.pi * frequency * t / sample_rate) for t in xrange(0, duration_in_samples)]

这应该是基本的正弦波知识。frequency是你想要的频率,单位是赫兹(Hz)。sample_rate是生成音频时每秒的样本数:44100赫兹是一个常见的值。duration_in_samples是音频的长度。(如果你的音频采样率是44100赫兹,那么5 * 44100就等于5秒。)

看起来你可能需要在把samples传给pygame.sndarray.samples之前,将它转换成numpy.array。文档上提到音频格式应该和pygame.mixer.get_init返回的格式匹配,所以要相应地调整samples,但这就是基本的思路。(mixer.get_init会告诉你上面提到的sample_rate,以及你是否需要考虑立体声,还要不要调整波形的幅度或偏移。)

确保samples的长度是完整的波形数量,这样它就可以循环播放了。

撰写回答