Actuellement, il ne semble y avoir aucun logiciel libre qui puisse utiliser le spectre audio (forme d'onde dans la gamme de fréquences qui se déplace de manière gluante en fonction des sons courants) sur mac. Alors, faisons le nôtre en utilisant python et jouons avec.
(Pour Windows, il semble que vous puissiez le faire avec un logiciel gratuit appelé AviUtl.)
J'ai un fichier au format ** wav ** pour lequel je souhaite créer une forme d'onde vocale. (Dans mon cas, c'est le fichier de sortie de la chanson que j'ai tapée dans GarageBand.) Je voudrais le convertir en format vidéo, mais c'est un peu terne pour une vidéo qui ne contient que du son dans une image fixe.
Par conséquent, le but de cette fois est de créer un ** spectre audio ** qui se déplace en fonction de la chanson que vous avez créée pour qu'elle ressemble plus ou moins à une vidéo.
Vous pouvez faire quelque chose comme ça ↓ https://www.youtube.com/watch?v=JPE54SlF6H0
OS:macOS High Sierra 10.13.6 Langage: Python 3.7.4
Autre que la bibliothèque standard,
Nécessite une installation. Fondamentalement, je pense que pip (pip3) est OK. Je vais l'écrire pour que même ceux qui ne comprennent que NumPy puissent le lire (parce que je le suis).
Ceci est un exemple de code qui ne déplace que des ondes bleu clair sur un fond rose.
Si vous préparez une source sonore nommée sample.wav
dans la même couche que le programme, vous pouvez la déplacer pour le moment. La stéréo monaurale est assez rudimentaire, mais les deux sont pris en charge. [Source sonore gratuite comme celle-ci](https://on-jin.com/sound/listshow.php?pagename=ta&title=%E3%82%B3%E3%83%B3%E3%83%88%E3%81 % AE% E3% 82% AA% E3% 83% 8102% EF% BC% 88% E3% 83% 81% E3% 83% A3% E3% 83% B3% E3% 83% 81% E3% 83% A3 % E3% 83% B3% EF% BC% 89 & janl =% E3% 81% 9D% E3% 81% AE% E4% BB% 96% E9% 9F% B3 & bunr =% E3% 83% 90% E3% 83% A9 % E3% 82% A8% E3% 83% 86% E3% 82% A3 & kate =% E3% 81% 9D% E3% 81% AE% E4% BB% 96) Vous pouvez également jouer.
Après avoir posté le tout, je voudrais regarder de plus près.
SampleAudioVisualizer.py
#!/usr/bin/env python3
import wave
import sys
import pygame
from pygame.locals import *
import scipy.fftpack as spfft
import soundfile as sf
import pyaudio
import numpy as np
# --------------------------------------------------------------------
#Paramètres
# --------------------
fn = "sample.wav"
#pour le calcul
CHUNK = 1024 #Sortie pour diffuser en morceaux avec pyaudio(Je ne sais pas pourquoi 1024)
start = 0 #Position de départ d'échantillonnage
N = 1024 #Nombre d'échantillons FFT
SHIFT = 1024 #Nombre d'échantillons pour déplacer la fonction de fenêtre
hammingWindow = np.hamming(N) #Fonction de fenêtre
# --------------------
#Pour dessiner
SCREEN_SIZE = (854, 480) #Taille d'affichage
rectangle_list = []
# --------------------
#Paramètres initiaux de l'écran Pygame
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Pygame Audio Visualizer")
# --------------------------------------------------------------------
#Redraw fonction de redessiner définie plus tard lors de la lecture du fichier wav()Fonction à appeler
def play_wav_file(filename):
try:
wf = wave.open(filename, "r")
except FileNotFoundError: #Si le fichier n'existe pas
print("[Error 404] No such file or directory: " + filename)
return 0
#Ouvrir le flux
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
#Lire l'audio
data = wf.readframes(CHUNK)
while data != '':
stream.write(data)
data = wf.readframes(CHUNK)
redraw()
stream.close()
p.terminate()
# --------------------------------------------------------------------
#Répétez "Dessin avec FFT".
def redraw():
global start
global screen
global rectangle_list
# --------------------
#Calculez le spectre d'amplitude en appliquant la FFT au bloc du point d'échantillonnage cible
windowedData = hammingWindow * x[start:start + N] #Bloc de données avec fonction fenêtre
X = spfft.fft(windowedData) # FFT
amplitudeSpectrum = [np.sqrt(c.real ** 2 + c.imag ** 2)
for c in X] #Spectre d'oscillation
# --------------------
#Dessin dans Pygame
screen.fill((240, 128, 128)) #Initialisez avec votre couleur préférée
rectangle_list.clear() #Initialisation de la liste carrée
#Dessin spectral Lors de l'exécution et du réglage des valeurs numériques
for i in range(86):
rectangle_list.append(pygame.draw.line(screen, (102, 205, 170), (1+i * 10, 350 + amplitudeSpectrum[i] * 1),
(1+i * 10, 350 - amplitudeSpectrum[i] * 1), 4))
pygame.display.update(rectangle_list) #Afficher la mise à jour
start += SHIFT #Décale la plage pour appliquer la fonction de fenêtre
if start + N > nframes:
sys.exit()
for event in pygame.event.get(): #Terminer le traitement
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
sys.exit()
# --------------------------------------------------------------------
if __name__ == "__main__":
# --------------------
#Obtenir des données wav
data, fs = sf.read(fn) #La forme des données est(Nombre d'images x nombre de canaux)
if data.ndim == 1:
x = data #S'il est monophonique, utilisez-le tel quel
if data.ndim == 2:
x = data[:, 0] #Si c'est stéréo, je me concentrerai uniquement sur le canal L(Pour R, changez 0 en 1)
nframes = x.size #Obtenez le nombre d'images(Utilisé comme condition de fin lors du décalage de la fonction de fenêtre dans FFT)
# --------------------
#Lancer la lecture et le dessin
play_wav_file(fn)
# --------------------------------------------------------------------
La partie données au format wav est une série de données chronologiques qui contient des informations sonores pour chaque ** 1 / fs ** seconde (fs: fréquence d'échantillonnage [Hz]).
(Ajout) Pour faciliter l'obtention d'une image, [Source sonore gratuite](https://on-jin.com/sound/listshow.php?pagename=ta&title=%E3%82%B3%E3%83] % B3% E3% 83% 88% E3% 81% AE% E3% 82% AA% E3% 83% 8102% EF% BC% 88% E3% 83% 81% E3% 83% A3% E3% 83% B3 % E3% 83% 81% E3% 83% A3% E3% 83% B3% EF% BC% 89 & jarn =% E3% 81% 9D% E3% 81% AE% E4% BB% 96% E9% 9F% B3 & bunr = % E3% 83% 90% E3% 83% A9% E3% 82% A8% E3% 83% 86% E3% 82% A3 & kate =% E3% 81% 9D% E3% 81% AE% E4% BB% 96) Tracons les données de. (Puisque cette source sonore est stéréo, je ne prendrai que le canal L) Comme ça, vous pouvez voir que ces données (tableau) contiennent des ondes qui prennent des valeurs de -1 à +1. L'axe horizontal est l'index du tableau. Puisque l'information de «1 / fs» secondes (d'ailleurs, «fs = 44.1 [kHz]» dans cet exemple) est exprimée pour chaque élément, c'est la «forme d'onde vue sur l'axe des temps». ..
Il peut être plus facile de dire que vous pouvez convertir en secondes en multipliant l'axe horizontal de ce graphique par «1/44100».
Le spectre audio, en revanche, est un graphique en constante évolution dans la gamme de fréquences. ** Les données dans la région de temps peuvent être visualisées dans la région de fréquence en effectuant une transformation de Fourier **, il semble donc que nous allons procéder tout en faisant bon usage de la transformation de Fourier.
Donc,
Il semble qu'il serait bon d'effectuer le traitement. C'est une image qui répète fréquemment la reproduction vocale et la conversion de Fourier en temps réel.
Au fait, j'essaye de traiter les points de données wav en décalant 1024 par 1024 comme "données à court terme", mais il n'est pas nécessaire que ce soit 1024 séparément. Cependant, si vous le rendez trop petit, il faudra plus de temps pour dessiner que pour jouer, donc le comportement sera étrange, et si vous le rendez trop grand, la mise à jour du dessin sera lente et la douceur sera perdue, alors soyez prudent.
Cette partie de la routine principale.
Extrait
import soundfile as sf
fn = "sample.wav"
# (Abréviation)
# --------------------------------------------------------------------
if __name__ == "__main__":
# --------------------
#Obtenir des données wav
data, fs = sf.read(fn) #La forme des données est(Nombre d'images x nombre de canaux)
if data.ndim == 1:
x = data #S'il est monophonique, utilisez-le tel quel
if data.ndim == 2:
x = data[:, 0] #Si c'est stéréo, je me concentrerai uniquement sur le canal L(Pour R, changez 0 en 1)
nframes = x.size #Obtenez le nombre d'images(Il est utilisé comme condition de fin lors du décalage de la fonction de fenêtre dans FFT décrite plus loin.)
# --------------------
# (Abréviation)
Vous pouvez utiliser PySoundFile pour gérer correctement les fichiers wav. J'ai pu obtenir les données et leur longueur en utilisant la méthode read ()
.
(Référence: opération de fichier Wav en Python)
Définissez une fonction appelée play_wav_file ()
qui écrit dans le flux et lit l'audio en unités de CHUNK
. Le module utilise wave et PyAudio.
(Référence: [Python] Lire le fichier wav avec Pyaudio)
Fondamentalement, c'est comme l'article auquel j'ai fait référence, mais j'ai mis une fonction auto-créée appelée redraw ()
dans le processus en boucle d'écriture dans le flux et de lecture des données suivantes. (Pour afficher le spectre audio en même temps que la lecture)
Extrait
import wave
import pyaudio
# --------------------------------------------------------------------
#Paramètres
# --------------------
#pour le calcul
CHUNK = 1024 #Sortie pour diffuser en morceaux avec pyaudio(Je ne sais pas pourquoi 1024)
# ~Omission~
# --------------------------------------------------------------------
#Lors de la lecture du fichier wav, la fonction de redraw redraw définie ultérieurement()Fonction à appeler
def play_wav_file(filename):
try:
wf = wave.open(filename, "r")
except FileNotFoundError: #Si le fichier n'existe pas
print("[Error 404] No such file or directory: " + filename)
return 0
#Ouvrir le flux
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
#Lire l'audio
data = wf.readframes(CHUNK)
while data != '':
stream.write(data)
data = wf.readframes(CHUNK)
redraw() #C'est une fonction pour redessiner, je la ferai plus tard.
stream.close()
p.terminate()
# ~Omission~
Cet article (Transformée de Fourier à court terme - Une percée sur l'intelligence artificielle) est très facile à comprendre et a été utile.
Puisque nous avons défini CHUNK = 1024
au moment de la lecture audio plus tôt, nous avons également défini le nombre d'échantillons cibles N à soumettre à une transformation de Fourier à grande vitesse (ci-après appelée FFT) à 1024.
Après avoir extrait 1024 éléments de données de l'ensemble des données, au lieu d'exécuter la FFT telle quelle, appliquez la ** fonction de fenêtre **, puis exécutez la FFT. C'est devenu une histoire théorique, mais la page intitulée Raison de l'utilisation de la fonction de fenêtre - Logical Arts Research Institute présentée dans l'article précédent. Il est organisé de manière facile à comprendre, veuillez donc y jeter un coup d'œil si vous êtes intéressé.
Ici, nous utilisons la grande ** fenêtre bourdonnante ** (np.hamming ()
). En appliquant cela, les bords sont connectés en douceur et l'échantillon découpé devient une fonction périodique.
Extrait
import sys
import scipy.fftpack as spfft
import numpy as np
# --------------------------------------------------------------------
#Paramètres
# --------------------
#pour le calcul
CHUNK = 1024 #Sortie pour diffuser en morceaux avec pyaudio(Je ne sais pas pourquoi 1024)
start = 0 #Position de départ d'échantillonnage
N = 1024 #Nombre d'échantillons FFT
SHIFT = 1024 #Nombre d'échantillons pour déplacer la fonction de fenêtre
hammingWindow = np.hamming(N) #Fonction de fenêtre
# ~Omission~
# --------------------------------------------------------------------
#Répétez "Dessin avec FFT". Ici, nous ne regarderons que le processus d'application de la FFT.
def redraw():
global start
# ~Omission~
# --------------------
#Calculez le spectre d'amplitude en appliquant la FFT au bloc du point d'échantillonnage cible
windowedData = hammingWindow * x[start:start + N] #Bloc de données avec fonction fenêtre
# (↑ liste x[]Cet article est-il 3-Ce sont les données wav extraites en 1.)
X = spfft.fft(windowedData) # FFT
amplitudeSpectrum = [np.sqrt(c.real ** 2 + c.imag ** 2)
for c in X] #Spectre d'oscillation
# --------------------
#Voici le processus de dessin dans Pygame(Omis ici)
start += SHIFT #Décale la plage pour appliquer la fonction de fenêtre
if start + N > nframes:
sys.exit() #Allez à la fin du fichier wav et terminez lorsque vous ne pouvez pas appliquer la fonction de fenêtre
#Voici les conditions de fin de PyGame(Omis ici)
# --------------------------------------------------------------------
# ~Omission~
Ce que vous faites est simple, échantillonnez N
éléments de données, appliquez une fonction de fenêtre pour effectuer la FFT, calculez le spectre d'amplitude, décalez la cible d'échantillonnage de SHIFT
et préparez-vous pour le prochain appel. Je vais. Il ne vous reste plus qu'à dessiner le spectre d'amplitude calculé en utilisant ** PyGame **.
Je vais jouer avec cet article (Visualiseur pour débutants avec Python).
Extrait
import pygame
from pygame.locals import *
# --------------------------------------------------------------------
#Paramètres
# --------------------
# ~Omission~
# --------------------
#Pour dessiner
SCREEN_SIZE = (854, 480) #Taille d'affichage
rectangle_list = []
# --------------------
#Paramètres initiaux de l'écran Pygame
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Pygame Audio Visualizer")
# --------------------------------------------------------------------
#Répétez "Dessin avec FFT".
def redraw():
# ~Omission~
global screen
global rectangle_list
# --------------------
#La FFT est appliquée au bloc du point d'échantillonnage cible et au spectre d'amplitude(amplitudeSpectrum)Traitement pour calculer(réduction)
# --------------------
#Dessin dans Pygame
screen.fill((240, 128, 128)) #Initialisez avec votre couleur préférée
rectangle_list.clear() #Initialisation de la liste carrée
#Dessin spectral Lors de l'exécution et du réglage des valeurs numériques
for i in range(86):
rectangle_list.append(pygame.draw.line(screen, (102, 205, 170), (1+i * 10, 350 + amplitudeSpectrum[i] * 1),
(1+i * 10, 350 - amplitudeSpectrum[i] * 1), 4))
pygame.display.update(rectangle_list) #Afficher la mise à jour
# ~Omission~
for event in pygame.event.get(): #Terminer le traitement
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
sys.exit()
# --------------------------------------------------------------------
# ~Omission~
Le problème est de savoir comment afficher les vagues, mais si vous utilisez pygame.draw.line
, par exemple, il semble que vous puissiez exprimer les vagues avec plusieurs lignes droites de la même manière qu'un histogramme. Je pense que les arrangements fonctionneront autant que vous le souhaitez ici. Les méthodes de PyGame sont organisées en ici. Il semble que pygame.draw.line
soit utilisé comme ça.
pygame.draw.line Tracez un segment de ligne droite.
pygame.draw.line(Surface, color, start_pos, end_pos, width=1): return Rect Tracez une ligne droite sur la surface. Il n'y a pas de décoration spéciale aux deux extrémités de la ligne, et cela devient une forme carrée qui correspond à l'épaisseur de la ligne.
À titre d'exemple du flux de dessin, déterminez la taille de la fenêtre PyGame à l'avance, initialisez-la, puis
pygame.Rect
) basé sur le spectre d'amplitude calculé et conservez-le dans la liste.pygame.display.update ()
C'est comme ça? Préparons également le processus de terminaison lorsque la fenêtre PyGame est effacée avec le bouton × ou que la touche esc est enfoncée.
(À propos, la taille d'affichage est définie sur 854 * 480 pour correspondre au rapport hauteur / largeur de youtube, et la plage de boucle for est définie sur 86 lorsque l'espacement entre les carrés (lignes droites) représentant les ondes créées cette fois est de 87. C'est parce qu'il disparaît de l'écran après les yeux. La description ici n'est pas très intelligente ... Je suis désolé. Si vous jouez en changeant les nombres de manière appropriée, je pense que vous pouvez en quelque sorte saisir le comportement. )
Dans l'exemple de code, seules les vagues se déplacent dans la couleur d'arrière-plan, mais vous pouvez également mettre des images de caractères et de logos comme le gif d'ouverture. (Référence: Introduction à Pygame avec Python 3: Chapitre 1) Facile à faire Surface.blit ()
dans redraw ()
Je pense que cela peut être mis en œuvre.
Aussi, cette fois, j'ai fait de mon mieux pour faire une vidéo en enregistrant l'écran qui a été créé, mais il semble que certaines personnes font des choses comme écrire l'écran de PyGame sur une vidéo. [PyGame] Exportation AVI et capture d'écran de l'écran
Je n'ai touché Python que dans les cours universitaires dans la mesure où je joue avec des exemples de code, mais c'est intéressant car il existe diverses bibliothèques utiles. Il y a peut-être eu de nombreux endroits où je ne savais pas comment le faire, mais j'espère pouvoir l'étudier petit à petit.
Je vous remercie pour votre travail acharné!