简单的Pygame音频频率问题

9
我该如何使用Pygame中的音频创建一个持续流畅演奏440Hz的声音?我认为这应该很容易,但我不想使用任何愚蠢的文件来完成任务。最终打算是在按住某个键时播放一段音符,就像我之前问过的一样。非常感谢您的帮助,因为我已经浪费了大量时间寻找答案。
2个回答

10

在遇到太多的“ValueError: Array depth must match number of mixer channels”和其他类似错误之后,我发现了这个可行的例子http://www.mail-archive.com/pygame-users@seul.org/msg16140.html。它能正确生成一个多维的16位整数数组,可以与立体声混音器一起使用。下面是一个最小工作示例,大部分代码从前面的链接中提取并加上必要的pygame代码。在Python 2.7.2中测试过,使用的pygame版本为'1.9.1release'。

这个示例将在立体声设置中从一个扬声器播放440 Hz的音调,从另一个扬声器播放550 Hz的音调。经过一些持续时间的试验,我发现如果将“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()

这很好 - 但它会产生一些奇怪的行为。有时pygame同时通过两个扬声器播放两个音调,但有时它会播放一系列的两个音调(并且通过两个扬声器播放一个相同的序列)。不确定该如何解决这个问题。 - David Stein
这在我的Windows 10上使用Python 3.9完美运行。 - Love and peace - Joe Codeswell

5
有哪种440 Hz的声音?"440 Hz"有很多类型的波形:正弦、锯齿、方波等。也有可能是长笛演奏的A音频。
假设你想要一个正弦波,似乎可以使用pygame.sndarray.samples来生成一个声音对象。(我没有测试过)您可以使用以下代码创建样本:
samples = [math.sin(2.0 * math.pi * frequency * t / sample_rate) for t in xrange(0, duration_in_samples)]

我希望这个是基础正弦波的内容。 frequency 是所需频率,单位为赫兹。 sample_rate 是在生成音频时每秒采样数: 44100 Hz 是一个典型值。 duration_in_samples 是音频长度。(如果您的音频采样率为 44100 Hz,则 5 * 44100 == 5 秒。)
看起来您可能需要在传递给 pygame.sndarray.samples 之前将 samples 转换为 numpy.array。文档指出,音频应匹配由 pygame.mixer.get_init 返回的格式,因此请适当调整 samples ,但这就是基本思路。mixer.get_init 将告诉您上面的 sample_rate 变量,以及是否需要考虑立体声,以及是否需要调整波的振幅或移位。
使 samples 成为波长的整数倍,并且它应该循环。

我该如何让声音无限循环播放? - Paul
通过在播放时告诉它无限循环来实现。你尝试过阅读文档吗?(pygame.org/docs)具体而言,您需要访问http://pygame.org/docs/ref/mixer.html#Sound.play - Thanatos

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