Essayez de créer une forme d'onde (spectre audio) qui se déplace en fonction du son avec python

introduction

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.)

Situation et objectif

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 [Pokemon Sword Shield] Combat! Beat [Arrangement de source sonore 8 bits] AudioVissualizer.gif

1. À propos de l'environnement

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).

2. Exemple de code

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)
# --------------------------------------------------------------------

3. Flux de mise en œuvre

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) Figure_1.png 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,

  1. Lire des données ultra-courtes ... (index 0 à 1023)
  2. Lors de la lecture de l'audio avec ** PyAudio ** ...
  3. Transformation de Fourier à grande vitesse (** FFT **) des données lues et dessin du spectre transformé avec ** PyGame ** ...
  4. Lisez également les données courtes suivantes ... (index 1024 à 2047)
    • (Répétez ceci jusqu'à ce que les données soient terminées) *

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.

3-1. Obtenir les données de série chronologique du fichier wav et sa longueur (nombre d'images)

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)

3-2. Processus de lecture de fichier Wav

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~

3-3. Appliquer FFT au bloc de points d'échantillonnage cibles

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. 1024px-Window_function_(hamming).svg.png

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 **.

3-4. Dessin avec 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

  1. Décidez de la couleur d'arrière-plan et initialisez l'écran PyGame (les couleurs peuvent être vues sur ces sites / Liste d'échantillons de couleurs WEB)
  2. Créez un objet droit (pygame.Rect) basé sur le spectre d'amplitude calculé et conservez-le dans la liste.
  3. Mise à jour de l'écran (= dessin) avec 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. )

4. Autre

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

5. Impressions

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é!

Recommended Posts

Essayez de créer une forme d'onde (spectre audio) qui se déplace en fonction du son avec python
Probablement le moyen le plus simple de créer un pdf avec Python 3
Essayez de créer un environnement python avec Visual Studio Code et WSL
Essayez de résoudre le diagramme homme-machine avec Python
Créer une page qui se charge indéfiniment avec python
Essayez de créer un code de "décryptage" en Python
Étapes pour créer un bot Twitter avec Python
Essayez de créer un groupe de dièdre avec Python
Essayez de résoudre le problème du voyageur de commerce avec un algorithme génétique (code Python)
Faisons un outil de veille de commande avec python
Comment créer un sous-menu avec le plug-in [Blender]
Essayez de résoudre le problème d'affectation du médecin de formation avec Python
Un mémo que j'ai touché au magasin de données avec python
[Python] Comment créer un histogramme bidimensionnel avec Matplotlib
Créer un répertoire avec python
Essayez de jouer avec l'uprobe qui prend directement en charge Systemtap
Créez un bot Mastodon avec une fonction pour répondre automatiquement avec Python
Créons un script qui s'enregistre avec Ideone.com en Python.
Essayez d'ouvrir une sous-fenêtre avec PyQt5 et Python
Créez un Twitter BOT avec le SDK GoogleAppEngine pour Python
Essayez d'automatiser le fonctionnement des périphériques réseau avec Python
Créer un message correspondant à la localisation avec la chaîne de traduction python
Essayez de déchiffrer les caractères déformés dans le nom du fichier joint avec Python
Un script qui facilite la création de menus riches avec l'API de messagerie LINE
Essayez de visualiser les nutriments des flocons de maïs que le champion de M-1 Milkboy a dit avec Python
Essayez de générer une image de veste de type death metal avec DCGAN + grattez le site de base de données de métaux pour cela
Jouez des sons en Python en supposant que le clavier est un clavier de piano
Je souhaite utiliser un caractère générique que je souhaite décortiquer avec Python remove
[Python] Créez un programme qui supprime les sauts de ligne dans le presse-papiers + Enregistrez-vous comme raccourci avec Windows
Essayez d'exploiter Facebook avec Python
Créez un script shell pour exécuter le fichier python plusieurs fois
[Python] Un mémo que j'ai essayé de démarrer avec asyncio
Créez un sélecteur de couleurs pour la roue chromatique avec Python + Qt (PySide)
Créez un environnement virtuel avec Python!
Essayez d'extraire une chaîne de caractères d'une image avec Python3
Comment gérer le problème du déplacement du répertoire actuel lorsque Python est exécuté depuis Atom
Les utilisateurs de Rails essaient de créer un moteur de blog simple avec Django
J'ai essayé de créer une liste de nombres premiers avec python
J'ai fait quelque chose avec python qui NOW LOADING se déplace de gauche à droite sur le terminal
Créer une API REST qui renvoie l'heure actuelle avec Python3 + Falcon
[Ev3dev] Créez un programme qui capture LCD (écran) en utilisant python
[LINE Messaging API] Créez un BOT qui se connecte à quelqu'un avec Python
Je voulais résoudre le problème ABC164 A ~ D avec Python
Une histoire qui n'a pas fonctionné lorsque j'ai essayé de me connecter avec le module de requêtes Python
Un script qui renvoie 0, 1 attaché au premier Python prime
5 façons de créer un chatbot Python
Essayez de produire de l'audio avec M5 STACK
Essayez de résoudre l'itinéraire le plus court avec les données sociales Python + NetworkX +
Essayez d'ajouter un mur à votre fichier IFC avec IfcOpenShell python
Comment envoyer une requête à l'API DMM (FANZA) avec python
[python] Une note que j'ai commencé à comprendre le comportement de matplotlib.pyplot
Essayez de créer un article de Qiita avec l'API REST [Préparation environnementale]
L'histoire de la création d'un module qui ignore le courrier avec python
Créer une API REST pour faire fonctionner dynamodb avec le Framework Django REST