Синхронизация аудио и анимации в python - PullRequest
1 голос
/ 08 апреля 2020

Я написал код для загрузки аудиофайла, вычисления частотного спектра и его анимации. Похоже, у меня нет возможности синхронизировать звук с анимацией с помощью инструментов, которые я использую.

Огромный барьер, на который я вхожу, заключается в том, что pydub на самом деле не говорит мне, где в аудио я я (хотя я могу рассчитать время) и matplotlib не дает мне никакого контроля над тем, где я нахожусь в анимации, и не дает мне гарантированную частоту кадров.

Есть ли техника или комбинация инструменты, которые мне не хватает, которые позволили бы мне решить эту конкретную проблему?

Код ниже:

from pydub import AudioSegment
from pydub.playback import play
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy import signal
import numpy as np
import threading
import time
from datetime import timedelta

# Load the audio and get the raw data for transformation
sound = AudioSegment.from_mp3("A Day Without Rain - Enya - Flora's Secret.mp3")
sampling_rate = sound.frame_rate
song_length = sound.duration_seconds
left = sound.split_to_mono()[0]
x = left.get_array_of_samples()

# Fourier transform
f, t, Zxx = signal.stft(x, fs=sampling_rate, nperseg=8820, noverlap=5292)
y = np.abs(Zxx.transpose())

# Setup a separate thread to play the music
music_thread = threading.Thread(target=play, args=(sound,))

# Build the figure
fig = plt.figure(figsize=(14, 6))
plt.style.use('seaborn-bright')
ax = plt.axes(xlim=[0, 4000], ylim=[0, 3000])
line1, = ax.plot([], [])


# Matplotlib function to initialize animation
def init():
    global annotation1, annotation2
    line1.set_data([], [])
    annotation1 = plt.annotate("Music: {}".format(""), xy=(0.2, 0.8), xycoords='figure fraction')
    annotation2 = plt.annotate("Animation: {}".format(""), xy=(0.6, 0.8), xycoords='figure fraction')
    return line1,


# Function for the animation
def animate(i):
    global music_start, annotation1, annotation2
    line1.set_data(f, y[i])
    if i == 0:
        music_thread.start()
        music_start = time.perf_counter()
    annotation1.set_text("Music: {}".format(timedelta(seconds=(time.perf_counter() - music_start))))
    annotation2.set_text("Animation: {}".format(timedelta(seconds=i / t.size * song_length)))
    return line1,


anim = FuncAnimation(fig, animate, init_func=init, interval=55)
plt.show()

1 Ответ

1 голос
/ 09 апреля 2020

Ну, я нашел один способ решить мою проблему.

Оказывается, проще всего изменить индекс кадра в функции animate перед установкой данных строки:

i = round((time.perf_counter() - music_start)/song_length * t.size)
...