根据音乐的节奏同步gif,结果比预期的时间短。

问题描述 投票:1回答:1

我正在尝试将gif与Spotify上播放的音乐节拍同步,但我在同步时遇到了速度问题。我一定是疯了,因为我找不到为什么不能工作的原因。以下是我的方法。

  • 以初始BPM(如: 150)为基础,然后找到BeatsSecond(即:BPS)
    • BPS = BPM / 60
  • 找到秒拍(SPB)的BeatsSecond (BPS)
    • SPB = 1 / BPS
  • 寻找秒速时时彩开奖走势图(SPL)乘以BeatsLoop的数量(BPL)中的.gif
    • SPL = SPB * BPL
  • 转换秒圈 (SPL)到毫秒循环(MSPL)
    • MSPL = SPL * 1000
  • 除以毫秒循环(MSPL)乘以帧数(num_frames)中的.gif来计算一帧所需的时间(frame_time),四舍五入到最接近的偶数,因为.gif帧时间最多只能精确到整毫秒。
    • frame_time = MSPL / num_frames
  • 将总的帧数相加(actual_duration),并在各帧中循环加减1毫秒,一直到 actual_duration 匹配 ceil(MSPL) (总是优先考虑较长的实际期限而不是较短的期限)
    difference = MSPL - actual_duration
    if not math.isclose(0, difference):
        # Add the difference and always prioritize longer duration compared to real duration value
        correction = int(math.ceil(difference))
        for i in range(0, abs(correction)):
            # Add/subtract corrections as necessary to get actual duration as close as possible to calculated duration
            frame_times[i % len(frame_times)] += math.copysign(1, correction)
    

现在,从这一点来看,gif的实际MillisecondsLoop应该总是等于MSLP或大于MSLP。然而,当我用指定的帧时间保存.gif时,如果修正值不是0,那么.gif总是以比预期更快的速度播放。我注意到,当使用其他在线服务,提供相同的 "同步gif到音乐 "功能,也是这种情况,所以它不只是我疯了,我想。

下面是用于获取帧时间的实际代码。

def get_frame_times(tempo: float, beats_per_loop: int, num_frames: int):
    # Calculate the number of seconds per beat in order to get number of milliseconds per loop
    beats_per_sec = tempo / 60
    secs_per_beat = 1 / beats_per_sec
    duration = math.ceil(secs_per_beat * beats_per_loop * 1000)
    frame_times = []
    # Try to make frame times as even as possible by dividing duration by number of frames and rounding
    actual_duration = 0
    for _ in range(0, num_frames):
        # Rounding method: Bankers Rounding (round to the nearest even number)
        frame_time = round(duration / num_frames)
        frame_times.append(frame_time)
        actual_duration += frame_time
    # Add the difference and always prioritize longer duration compared to real duration value
    difference = duration - actual_duration
    if not math.isclose(0, difference):
        correction = int(math.ceil(difference))
        for i in range(0, abs(correction)):
            # Add/subtract corrections as necessary to get actual duration as close as possible to calculated duration
            frame_times[i % len(frame_times)] += math.copysign(1, correction)
    return frame_times

我是用PIL(Pillow)的图像模块来保存gif的。

frame_times = get_frame_times(tempo, beats_per_loop, num_frames)
frames = []
for i in range(0, num_frames):
  # Frames are appended to frames list here
# disposal=2 used since the frames may be transparent
frames[0].save(
    output_file, 
    save_all=True, 
    append_images=frames[1:], 
    loop=0, 
    duration=frame_times, 
    disposal=2)

我有什么地方做错了吗?我似乎找不到为什么这样做不行,为什么gif的实际持续时间比指定的帧时间短很多。这让我感觉稍微好一点,其他提供这个功能的网站服务最终也是同样的结果,但同时我觉得这绝对应该是可能的。

python audio gif
1个回答
0
投票

解决了! 我不知道这是.gif格式的限制还是PIL中图像模块的限制,但似乎帧时间只能精确到10毫秒的倍数。经检查修改后的图像的实际帧时间,它们被浮动到最接近10的倍数,导致整体播放速度比预期快。

为了解决这个问题,我修改了代码,以10为增量选择帧时间(必要时再次优先选择较长的实际持续时间),并将帧时间调整尽可能均匀地分散在整个列表中。

def get_frame_times(tempo: float, beats_per_loop: int, num_frames: int):
    # Calculate the number of seconds per beat in order to get number of milliseconds per loop
    beats_per_sec = tempo / 60
    secs_per_beat = 1 / beats_per_sec
    duration = round_tens(secs_per_beat * beats_per_loop * 1000)
    frame_times = []
    # Try to make frame times as even as possible by dividing duration by number of frames
    actual_duration = 0
    for _ in range(0, num_frames):
        frame_time = round_tens(duration / num_frames)
        frame_times.append(frame_time)
        actual_duration += frame_time
    # Adjust frame times to match as closely as possible to the actual duration, rounded to multiple of 10
    # Keep track of which indexes we've added to already and attempt to split corrections as evenly as possible
    # throughout the frame times
    correction = duration - actual_duration
    adjust_val = int(math.copysign(10, correction))
    i = 0
    seen_i = {i}
    while actual_duration != duration:
        frame_times[i % num_frames] += adjust_val
        actual_duration += adjust_val
        if i not in seen_i:
            seen_i.add(i)
        elif len(seen_i) == num_frames:
            seen_i.clear()
            i = 0
        else:
            i += 1
        i += num_frames // abs(correction // 10)
    return frame_times
© www.soinside.com 2019 - 2024. All rights reserved.