【问题标题】:Python: Inaccurate timing playing audio at a set BPM interval (<1s)Python:以设定的 BPM 间隔(<1s)播放音频的时间不准确
【发布时间】:2019-01-17 13:21:13
【问题描述】:

我正在尝试在 Raspberry Pi 上制作一个非常简单的节拍器,该节拍器以设定的间隔播放 .wav 文件,但时间听上去不准确。 实在想不通,是不是python的时间模块不准确?

我不认为处理播放音频的代码是瓶颈,因为如果我把它放在一个没有计时器的循环中,它会一直发出嘎嘎声。 使用下面的简单代码,声音会以节拍播放几次,然后随机关闭一个节拍,一遍又一遍。

import pygame
from time import sleep

pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.mixer.init()
pygame.init()

BPM = 160
sound = pygame.mixer.Sound('sounds/hihat1.wav')


while True:
    sound.play()
    sleep(60/BPM)

我希望声音每 X 毫秒重复一次,精度至少为 +/-10 毫秒左右。这不现实吗?如果是这样,请提出替代方案。

【问题讨论】:

  • 也许用不同的音频文件试试?只是为了确保sounds/hihat1.wav 的某个地方没有问题。
  • 我做了,不幸的是同样的问题
  • 嗯...也许可以试试sleep(60/BPM - sound.get_length()),以便暂停占音频文件的长度
  • 这几乎是 stackoverflow.com/questions/28528935/…stackoverflow.com/questions/26563033/… 的重复 - 不是 exact 重复,所以我暂时不投票关闭,但如果你找到其中之一链接解决了您的问题,请发表评论,以便我们将其作为重复项关闭。
  • 附带说明:sleep() 绝对是不是时间关键代码的正确工具,参见文档 (docs.python.org/2/library/time.html#time.sleep):“他实际的暂停时间可能小于请求的值,因为任何捕获的信号都将在执行该信号的捕获例程后终止 sleep()。此外,由于系统中其他活动的调度,暂停时间可能比请求的长任意数量。"

标签: python raspberry-pi pygame timing pyaudio


【解决方案1】:

问题原来是使用过大的块大小,这可能导致 pygame 播放声音较晚,因为早期的块已经排队。我的第一个建议是,我预计 OP 的代码会随着时间的推移慢慢漂移,这表明这样的事情会做得更好:

import pygame
from time import time, sleep
import gc

pygame.mixer.pre_init(44100, -16, 2, 256)
pygame.mixer.init()
pygame.init()

BPM = 160
DELTA = 60/BPM

sound = pygame.mixer.Sound('sounds/hihat1.wav')
goal = time()

while True:
    print(time() - goal)
    sound.play()
    goal += DELTA
    gc.collect()
    sleep(goal - time())

即跟踪“当前时间”并根据经​​过的时间调整sleeps。我在每次睡眠前明确执行“垃圾收集”(即gc.collect()),以使事情更具确定性。

【讨论】:

  • 感谢您的建议,我运行了您的代码,但仍然遇到同样的问题。打败我。我将在 bash 或 nodejs 中尝试相同的示例,看看是否可行。
  • @LouisT 我添加了垃圾收集和打印以查看进程认为它有多远……可能有帮助吗?我的笔记本电脑(不是 RPI)总是在 1 毫秒内(听起来不错)
  • 我认为 bash 会更糟,node 可能会更好,但我对此表示怀疑。我认为您需要使用 C 或 C++ 之类的低级语言才能获得更多确定性
  • 我会在我的桌面上运行代码,看看 rpi 是否是罪魁祸首,好主意
  • 只是想,你可能也想减少缓冲区大小——硬编码限制似乎是256 samples,但我不确定当缓冲区排队时 pygame 如何处理播放样本/飞行
【解决方案2】:

当我在本地机器上测试您的代码时,似乎 sleep 并不关心 pygame 线程,因此您的声音会自行重叠。

此外,我认为您应该使用 pygames 自己的计时器来延迟操作。

你能在你的py上试试下面的代码吗?

import pygame

pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.mixer.init()
pygame.init()

BPM = 160
sound = pygame.mixer.Sound('sounds/hihat1.wav')

while True:
    sound.play()
    pygame.time.delay(int(sound.get_length()*1000))
    pygame.time.delay(int(60/BPM*1000))

【讨论】:

  • 声音本身很短,如果你使用的声音比间隔长,你就会重叠。您可以将sound.play() 替换为pygame.mixer.Channel(0).play(sound),如果声音太长,声音会自行切断
  • 我认为这是因为 pygame 代码在另一个线程上,而 time.sleep 可能会把你搞砸。这就是为什么他们在 pygame 中具有延迟功能的原因。请检查更新的代码。
  • 我刚刚运行了你更新的代码,同样的事情,有些节拍仍然可以听到
  • 我忘记了 pygame 中的延迟是以毫秒为单位的。最后的编辑应该可以解决它。
  • 感谢您的帮助,不幸的是我刚刚运行它仍然无法正常工作。如果我这样做,似乎第 5 拍总是关闭,这对我来说没有意义:pygame.time.delay(int(60/BPM*1000) - int(sound.get_length()*1000))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 2014-04-04
  • 1970-01-01
相关资源
最近更新 更多