【问题标题】:How to play sound samples continuously in pygame?如何在pygame中连续播放声音样本?
【发布时间】:2021-04-16 18:41:09
【问题描述】:

我想播放一个连续的正弦波(暂时),它会根据鼠标指针的位置(暂时)改变其频率(暂时)。因此,我生成并播放一帧长度的波(下面代码中的 1/2 秒)。产生的声音在声音中有很小的间隙,足以非常显着。增加单个声音的持续时间没有帮助,更改缓冲区大小没有帮助,使用 dtick 代替 dt 没有帮助。

有没有办法以这种方式实现连续的声音?还是其他方式?

谢谢

版本:Python 3.9.3、pygame 2.0.1

最少(仍然很长)代码:

import numpy as np
import pygame
from pygame import Color
from pygame.locals import KEYDOWN, KMOD_CTRL, QUIT, K_q

fade_out = np.linspace(1, 0, 100)
fade_in = np.linspace(0, 1, 100)

bits = 16
sample_rate = 44100
max_sample = 2 ** (bits - 1) - 1


def gen_sound(duration, freq):
    n_samples = int(duration / 1000 * sample_rate)
    buf = np.zeros((n_samples, 2), dtype=np.int16)
    buf[:, 0] = (
        0.25
        * max_sample
        * np.sin(2 * np.pi * freq[0] * np.arange(n_samples) / sample_rate)
    )
    buf[:, 1] = (
        0.25
        * max_sample
        * np.sin(2 * np.pi * freq[1] * np.arange(n_samples) / sample_rate)
    )
    buf[-100:, 0] = (buf[-100:, 0] * fade_out).astype(int)
    buf[-100:, 1] = (buf[-100:, 1] * fade_out).astype(int)
    buf[:100, 0] = (buf[:100, 0] * fade_in).astype(int)
    buf[:100, 1] = (buf[:100, 1] * fade_in).astype(int)

    return pygame.sndarray.make_sound(buf)


def main(argv: list = None):

    # initialize sound
    pygame.mixer.pre_init(sample_rate, -bits, 2, 512)

    # initialize pygame
    pygame.init()
    size = (1000, 800)
    surf = pygame.display.set_mode(size)
    surf.fill(Color("black"))
    clock = pygame.time.Clock()

    # frames per second and duration of a frame in ms
    FPS = 2
    dt = 1000 / FPS
    dtick = 1000 / FPS
    # position of the mouse pointer, None means outside of the window
    mpos = None

    # event loop
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == QUIT:
                return
            # shutdown with C-q
            if e.type == KEYDOWN:
                if e.mod & KMOD_CTRL and e.key == K_q:
                    return

        if pygame.mouse.get_focused():
            mpos = pygame.mouse.get_pos()
        else:
            mpos = None

        freq = mpos if mpos else (440, 440)
        sound = gen_sound(dt, freq)
        sound.play(loops=0, fade_ms=0)

        pygame.display.update()
        dtick = clock.tick(FPS)


if __name__ == "__main__":
    main()

【问题讨论】:

  • 您可能希望对生成的波使用缓存,这样如果频率和持续时间没有改变,您就不会重新构建波。此外,如果声音在后台播放,您希望在声音停止时更新它(除非播放器中内置了声音队列,这很可能是。)而不是程序的每个循环。

标签: python pygame


【解决方案1】:

Sound数据可以边播放边直接修改,无需每帧调用.play()

def gen_sound(buf, freq):
    buf[:, 0] = (
            0.25
            * max_sample
            * np.sin(2 * np.pi * freq[0] * np.arange(n_samples) / sample_rate)
    )
    buf[:, 1] = (
            0.25
            * max_sample
            * np.sin(2 * np.pi * freq[1] * np.arange(n_samples) / sample_rate)
    )

主要是:

def main(argv: list = None):
    # ...

    clock = pygame.time.Clock()

    buf = np.zeros((n_samples, 2), dtype=np.int16)
    sound = pygame.sndarray.make_sound(buf)
    buf = pygame.sndarray.samples(sound) # This is required because make_sound copies the array, but we want a reference
    gen_sound(buf, (440, 440))
    sound.play(loops=-1, fade_ms=0)

    # ...
    # event loop
    while True:
        # ...

        freq = mpos if mpos else (440, 440)
        gen_sound(buf, freq)

        pygame.display.update()
        dtick = clock.tick(FPS)

请注意,这将遇到 here 描述和解决的问题,例如正弦波过渡不平滑。但这超出了这个问题和我的知识范围。

【讨论】:

  • 这就是我一直在寻找的技巧。我怀疑这是可能的,但不知道如何即时写入同一个缓冲区。是的,相位不匹配是另一个主题,感谢您的链接。但是在某些频率下,我会听到以前无法听到的连续声音。
猜你喜欢
  • 1970-01-01
  • 2023-01-24
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2017-08-15
  • 1970-01-01
相关资源
最近更新 更多