[PYTHON] Sauvegardez les données vocales acquises par le navigateur au format wav sur le serveur

Ceci est un mémo du résultat de diverses investigations pour voir s'il est possible d'envoyer facilement de l'audio au serveur à partir du microphone connecté au PC.

C'est pour un petit outil à usage interne, c'est donc une spécification qui "utilise simplement la dernière version de Google Chrome pour le moment".

C'est presque mon propre mémorandum et j'ai peu de connaissances en JavaScript. Python est souvent utilisé côté serveur et pour l'analyse des données.

Il existe peut-être une bonne bibliothèque JS pour la communication vocale, et le travail décrit ci-dessous peut être réalisé en un seul coup. Si vous le savez, faites-le nous savoir dans les commentaires!

Obtenez de l'audio avec un navigateur et envoyez-le avec une prise Web

Il s'agit de l'article de Google destiné aux développeurs WEB "Obtenir les données audio des utilisateurs" A été utile.

<script>  
  var handleSuccess = function(stream) {
    var context = new AudioContext();
    var input = context.createMediaStreamSource(stream)
    var processor = context.createScriptProcessor(1024, 1, 1);

    //Connexion WebSocket
    var connection = new WebSocket('wss://hogehoge.com:8000/websocket');

    input.connect(processor);
    processor.connect(context.destination);

    processor.onaudioprocess = function(e) {
      var voice = e.inputBuffer.getChannelData(0);
      connection.send(voice.buffer); //Envoyer par web socket
    };
  };

  navigator.mediaDevices.getUserMedia({ audio: true, video: false })
      .then(handleSuccess)
</script>

1. Comment continuer à envoyer de l'audio au serveur

Il semble y avoir trois façons d'envoyer de l'audio au serveur:

  1. Sondage avec http / https
  2. Envoyer par intermittence via la communication WebSocket (ws / wss)
  3. Utilisez WebRTC

Ici, vous pouvez lire l'article de Qiita "Technologie d'envoi du serveur au client - Focusing on WebSocket" et "Présentation de la technologie pour WebSocket / WebRTC »a été utile.

«Ce n'est pas bon de l'envoyer fréquemment par communication http, et il semble lourd d'utiliser WebRTC même si la communication vocale bidirectionnelle n'est pas nécessaire», j'ai donc décidé de continuer à envoyer le tampon vocal avec websocket pour le moment. fait. WebSocket semble être capable d'envoyer et de recevoir des binaires ou des chaînes.

2. AudioContext.createScriptProcessor détermine la taille de l'audio à envoyer en une seule fois

Pour plus d'informations, veuillez lire ce document.

La taille du tampon en unités de cadres d'échantillonnage. S'il est spécifié, il doit s'agir de l'une des valeurs suivantes: 256, 512, 1024, 2048, 4096, 8192, 16384. S'il n'est pas spécifié ou si 0 est spécifié, la valeur optimale pour l'environnement est définie. Cette valeur sera utilisée tant que le nœud survit, et sa valeur est 2 ci-dessus.

Cette valeur détermine la fréquence à laquelle les événements de processus audio se produisent et la taille de la trame d'échantillonnage transmise pour chaque événement. La spécification d'une petite valeur entraîne une faible latence et la spécification d'une valeur élevée évite la corruption audio et les problèmes. Il est recommandé de ne pas décider vous-même de cette valeur, mais de laisser l'implémentation la décider en termes de délai et de qualité.

Je ne connais pas la signification exacte du mot "autoriser l'implémentation à choisir une bonne taille de tampon", mais dois-je le définir sur 0?

Il est également abordé dans This Stack Overflow (2013).

3. Obtenez de l'audio avec AudioBuffer.getChannelData

Lisez également la Documentation. Nous avons affaire à un son monophonique ici, et si vous avez besoin d'un son stéréo, il semble que vous deviez le concevoir.

L'appel de buffer renverra des données audio de type Float32Array, c'est-à-dire des nombres réels compris entre -1 et + 1. Il convient de noter que dans les fichiers WAV (bien qu'il semble y avoir plusieurs formats), ils sont représentés par des entiers signés 16 bits, c'est-à-dire des valeurs comprises entre -32768 et 32767.

Enregistrer en tant que fichier WAV sur le serveur

J'ai utilisé Python 3.6, auquel je suis habitué.

J'ai utilisé Tornado, un framework WEB léger qui est bon pour le traitement asynchrone de Python.

import tornado.ioloop
import tornado.web
import tornado.websocket

import wave
import numpy as np


SAMPLE_SIZE = 2
SAMPLE_RATE = 48000
PATH = '/path/to/output.wav'


class WebSocketHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        self.voice = []
        print("opened")

    def on_message(self, message):
        self.voice.append(np.frombuffer(message, dtype='float32'))

    def on_close(self):
        v = np.array(self.voice)
        v.flatten()

        #Convertir en binaire en entier 16 bits et enregistrer
        arr = (v * 32767).astype(np.int16)
        with wave.open(PATH, 'wb') as wf:
            wf.setnchannels(1)
            wf.setsampwidth(SAMPLE_SIZE)
            wf.setframerate(SAMPLE_RATE)
            wf.writeframes(arr.tobytes('C'))
        
        self.voice.clear()
        print("closed")


app = tornado.web.Application([
    (r"/websocket", WebSocketHandler)
])

if __name__ == "__main__":
    app.listen(8000)
    tornado.ioloop.IOLoop.instance().start()

1. À propos de Tornado

La documentation du serveur WebSocket utilisant Tornado est ici.

D'autres frameworks tels que Flask, Bottle et Django sont célèbres, mais Tornado a été adopté car il est bon pour le traitement asynchrone comme node.js. De plus, il semblait y avoir beaucoup de code qui pourrait être utilisé comme référence par rapport à d'autres frameworks.

2. Binpy facile à lire

En utilisant numpy, une bibliothèque de calcul numérique en Python, vous pouvez facilement lire les données transmises et les lire comme un tableau de numpy.

Pour plus d'informations, lisez Numpy.frombuffer Reference.

3. À propos des fichiers wav

Il convient de noter que dans les fichiers WAV (bien qu'il semble y avoir plusieurs formats), ils sont représentés par des entiers signés 16 bits, c'est-à-dire des valeurs comprises entre -32768 et 32767.

De plus, la valeur du taux d'échantillonnage (48000) dépend de l'implémentation du côté JavaScript, vérifiez donc AudioContext.sampleRate. ..

J'avais des problèmes parce que je ne comprenais pas les spécifications de la structure de données WAV, mais "Programmation sonore commençant en langage C-Traitement du signal des effets sonores" J'ai étudié en lisant l'explication du chapitre 1.

Eh bien, après tout, j'ai réussi à utiliser la bibliothèque Python standard wave.

Résumé

c'est tout.

À partir de là, vous pouvez envoyer à l'API vocale de divers services cloud, effectuer un traitement vocal avec Python, etc. De plus, je n'ai fait aucune certification, donc je dois le faire correctement.

La raison d'utiliser WebSocket est qu'il serait intéressant de pouvoir pousser du serveur vers le client (navigateur) et renvoyer les résultats de l'API d'analyse vocale et du traitement vocal en temps réel.

Recommended Posts

Sauvegardez les données vocales acquises par le navigateur au format wav sur le serveur
Observation en virgule fixe de données spécifiques sur le Web en exécutant automatiquement le navigateur Web sur le serveur (Ubuntu16.04) (1) -Installation du navigateur Web-
Exporter le contenu acquis par Twitter Streaming API au format JSON
Observation en virgule fixe de données spécifiques sur le Web en exécutant automatiquement un navigateur Web sur le serveur (Ubuntu16.04) (2) -Web scraping-
Enregistrez l'arbre phylogénétique dessiné par la fonction draw_ascii de Bio.Phylo au format texte
Exécutez des tâches en arrière-plan sur le serveur sur lequel vous vous êtes connecté
Observation en virgule fixe de données spécifiques sur le Web en exécutant automatiquement le navigateur Web sur le serveur (Ubuntu16.04) (3) ~ Exécution automatique Cron ~
Éliminez les caractères japonais brouillés dans les données JSON acquises par l'API.
Test.py n'est pas reflété sur le serveur Web dans Python3.
Pour désactiver le cache du navigateur sur le serveur HTTP simple de Python
Remarques sur la coloration par valeur dans le diagramme de dispersion matplotlib
Comment retourner les données contenues dans le modèle django au format json et les mapper sur le dépliant
Gzip compresser les données en streaming
Écrire des données au format HDF
Données acquises par Django reliées