【问题标题】:Bad timing when playing audio files with PyGame使用 PyGame 播放音频文件时的错误时机
【发布时间】:2017-03-03 01:16:56
【问题描述】:

当我使用 PyGame 每 0.5 秒播放一次声音时:

import pygame, time

pygame.mixer.init()
s = pygame.mixer.Sound("2.wav")

for i in range(8):
  pygame.mixer.Channel(i).play(s)
  time.sleep(0.5)

它根本不正确地尊重时间。

好像有0.2秒的停顿比0.7秒又0.2秒,很不规律。


注意事项:

  • 我知道time.sleep()不是世界上最准确的,但即使有更准确的解决方案from here,问题仍然存在

  • 在树莓派上测试

  • 如果我播放许多不同的文件s[i].play(),问题仍然存在,其中 i 的范围很大。所以问题不在于它试图重播同一个文件这一事实

【问题讨论】:

  • "暂停 0.2 秒比 0.7 秒然后又是 0.2 秒,非常不规则。"这对我来说听起来不是很不规律,它听起来是周期性的。声音持续多久?特别是因为0.7 == (sleep_time + 0.2)
  • @roganjosh 为什么会有这样的循环? s.play() 是非阻塞的,应该继续代码
  • @roganjosh 我修改了代码以利用通道,因此播放的每个声音都不应干扰另一个声音。问题仍然存在...
  • 你试过没有停顿会有什么效果吗?我猜 0.7 是基于你的暂停是 0.5 秒的事实,所以基本上没有每 0.2 的暂停就会播放一个声音?你的音频文件有多长? (如果是 0.2 秒,也许它实际上是某种相关的)
  • 几乎用pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=512)解决了,但节奏还是有点笨拙。我无法获得完美的“节拍器”节拍。

标签: python audio pygame


【解决方案1】:

原因如下:

即使我们将音频缓冲区减少到声卡支持的最小值(1024 或 512 个样本,而不是 pygame 的默认值 4096),差异仍然存在,使得应该是“节拍器节拍”的不规则.

我会在找到可行的解决方案后立即更新。 (我对这个方向有一些想法)。

【讨论】:

    【解决方案2】:

    正如您在自己的回答中所写,计时问题的原因很可能是音频回调运行与应用程序的其余部分分离。

    音频后端通常有某种时钟,可以从回调函数内部和外部访问。 我看到了两种可能的解决方案:

    1. 使用允许您自己实现回调函数的库,在回调函数中计算声音的开始时间,将这些时间与“音频时钟”的当前时间进行比较,然后将声音写入在输出缓冲区的适当位置输出。

    2. 使用允许您指定开始播放声音的确切时间(根据“音频时钟”)的库。该库将为您完成上一点的步骤。

    对于第一个选项,您可以使用sounddevice 模块。回调函数(您必须实现)将获得一个名为time 的参数,该参数具有time.outputBufferDacTime 属性,这是一个浮点值,指定输出缓冲区的第一个样本的时间(以秒为单位)将被播放。

    完全披露:我是sounddevice 模块的作者,所以我的推荐相当有偏见。

    最近,我开始研究rtmixer 模块,它可用于第二个选项。 请注意,这是处于非常早期的开发状态,因此请谨慎使用。 有了这个模块,你就不用写回调函数了,你可以使用函数rtmixer.Mixer.play_buffer()在指定的时间(以秒为单位)播放一个音频缓冲区。作为参考,您可以从rtmixer.Mixer.time获取当前时间。

    【讨论】:

    • 很好的答案!谢谢!我几乎想到了同样的想法:从“播放声音事件”循环线程发送一个时间戳,音频回调将在“混合线程”中使用,并具有适当的位置。我肯定会使用sounddevice,我已经在我的项目中使用了samplerbox.org!谢谢
    • 很酷的东西,我印象深刻!
    • Re @Matthias,rtmixer 是否在 C 或 Cython(不是 cpython 而是 cython)中进行实际混合/求和以加快速度?这就是我为 SamplerBox (see here) 进行混音的方式,我想知道您是否也注意到在编译语言中进行混音数学部分也大大提高了您的性能。
    • @Basj 我已经在 C 中实现了整个音频回调函数(参见 rtmixer.c),它直接传递给 PortAudio 库(没有将其包装到 Python 函数中),部分是为了加速它up 但主要是为了避开 GIL。我喜欢你对 Cython 的使用,但最好摆脱共享状态并在 Cython 中实现整个回调函数。我们可能应该在 Github 上继续讨论,请随时在此处提及我 (@mgeier)。
    • 谢谢@Matthias。最后一件事(然后我将继续在 Github 上进行其他相关讨论):如何比较 time_info.outputBufferDacTime(确实非常有用)与事件触发的时间?例如time.time() 给我 1488896540.18 而outputBufferDacTime 给我 2344.26198273。如果它们不在同一个单元中,如何比较它们?
    猜你喜欢
    • 2022-08-23
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多