Avertissement: je ne considère aucun algorithme ni API pour travailler avec la reconnaissance du son et de la parole. Cet article concerne les problèmes audio et comment les résoudre avec Go.

phono
est un cadre d'application pour travailler avec le son. Sa fonction principale est de créer un convoyeur à partir de diverses technologies qui traitera le son pour toi de la manière dont vous avez besoin.
Qu'est-ce que le convoyeur a à voir avec cela, en plus de différentes technologies, et pourquoi un autre cadre? Voyons maintenant.
D'où vient le son?
En 2018, le son est devenu la manière standard dont les humains interagissent avec la technologie. La plupart des géants de l'informatique ont créé leur propre assistant vocal ou le font actuellement. Le contrôle vocal est déjà sur la plupart des systèmes d'exploitation, et la messagerie vocale est une caractéristique typique de tout messager. Dans le monde, environ un millier de startups travaillent sur le traitement du langage naturel et environ deux cents sur la reconnaissance vocale.
Avec la musique, une histoire similaire. Il joue à partir de n'importe quel appareil et l'enregistrement sonore est disponible pour tous ceux qui ont un ordinateur. Le logiciel musical est développé par des centaines d'entreprises et des milliers de passionnés à travers le monde.
Si vous deviez travailler avec du son, les conditions suivantes devraient vous être familières:
- L'audio doit être obtenu à partir d'un fichier, d'un appareil, d'un réseau, etc.
- L'audio doit être traité : ajouter des effets, transcoder, analyser, etc.
- L'audio doit être transféré vers un fichier, un appareil, un réseau, etc.
- Les données sont transmises dans de petits tampons.
Il en résulte un pipeline régulier - il existe un flux de données qui passe par plusieurs étapes de traitement.
Des solutions
Pour plus de clarté, prenons une tâche de la vie réelle. Par exemple, vous devez convertir une voix en texte:
- Nous enregistrons l'audio de l'appareil
- Supprimer le bruit
- Égaliser
- Passer le signal à l'API de reconnaissance vocale
Comme toute autre tâche, celle-ci a plusieurs solutions.
Le front
Hardcore uniquement cyclistes programmeurs. Nous enregistrons le son directement via le pilote de la carte son, écrivons une réduction intelligente du bruit et un égaliseur multibande. C'est très intéressant, mais vous pouvez oublier votre tâche d'origine pendant plusieurs mois.
Long et très difficile.
Normal
Une alternative consiste à utiliser les API existantes. Vous pouvez enregistrer de l'audio en utilisant ASIO, CoreAudio, PortAudio, ALSA et autres. Il existe également plusieurs types de plugins pour le traitement: AAX, VST2, VST3, AU.
Un large choix ne signifie pas que vous pouvez tout utiliser à la fois. En règle générale, les restrictions suivantes s'appliquent:
- Système d'exploitation Toutes les API ne sont pas disponibles sur tous les systèmes d'exploitation. Par exemple, AU est une technologie OS X native et n'est disponible que là-bas.
- Langage de programmation La plupart des bibliothèques audio sont écrites en C ou C ++. En 1996, Steinberg a publié la première version du SDK VST, toujours le standard de plugin le plus populaire. Après 20 ans, il n'est plus nécessaire d'écrire en C / C ++: pour VST il y a des wrappers en Java, Python, C #, Rust, et qui sait quoi d'autre. Bien que le langage reste une limitation, désormais même le son est traité en JavaScript.
- Fonctionnel. Si la tâche est simple et directe, il n'est pas nécessaire d'écrire une nouvelle application. Le même FFmpeg peut faire beaucoup.
Dans cette situation, la complexité dépend de votre choix. Dans le pire des cas, vous devez traiter avec plusieurs bibliothèques. Et si vous n'avez pas de chance, avec des abstractions complexes et des interfaces complètement différentes.
Quel est le résultat?
Vous devez choisir entre très complexe et complexe :
- soit gérer plusieurs API de bas niveau pour écrire vos vélos
- soit traiter avec plusieurs API et essayer de se faire des amis avec eux
Quelle que soit la méthode choisie, la tâche revient toujours au convoyeur. Les technologies utilisées peuvent varier, mais l'essence est la même. Le problème est qu'encore une fois, au lieu de résoudre un vrai problème, vous devez écrire le vélo bande transporteuse.
Mais il y a une issue.
phono

phono
créé pour résoudre des problèmes courants - « recevoir, traiter et transmettre » le son. Pour ce faire, il utilise le pipeline comme l'abstraction la plus naturelle. Il y a un article sur le blog officiel Go qui décrit le modèle de pipeline. L'idée principale du pipeline est qu'il existe plusieurs étapes de traitement des données qui fonctionnent indépendamment les unes des autres et échangent des données via des canaux. Ce dont vous avez besoin.
Pourquoi partir?
Premièrement, la plupart des programmes et bibliothèques audio sont écrits en C, et Go est souvent désigné comme son successeur. En outre, il existe des cgo et un bon nombre de classeurs pour les bibliothèques audio existantes. Vous pouvez prendre et utiliser.
Deuxièmement, à mon avis personnel, Go est une bonne langue. Je n'irai pas en profondeur, mais je noterai son multithreading . Les canaux et les gorutins simplifient considérablement la mise en œuvre du convoyeur.
Abstraction
Le cœur du phono
est le type pipe.Pipe
. C'est lui qui met en œuvre le pipeline. Comme dans l' exemple du blog , il existe trois types d'étapes:
pipe.Pump
(pompe anglaise) - réception du son, uniquement les canaux de sortiepipe.Processor
(processeur anglais) - traitement du son, canaux d'entrée et de sortiepipe.Sink
(English sink) - transmission du son, canaux d'entrée uniquement
À l'intérieur du pipe.Pipe
données dans des tampons. Règles de construction d'un pipeline:

- Un
pipe.Pump
- Plusieurs
pipe.Processor
placé séquentiellement les uns après les autres - Un ou plusieurs
pipe.Sink
placé en parallèle - Tous les composants des
pipe.Pipe
doivent avoir les mêmes:
- Taille de la mémoire tampon (messages)
- Taux d'échantillonnage
- Nombre de canaux
La configuration minimale est une pompe et un évier, le reste est facultatif.
Regardons quelques exemples.
Simple
Tâche: lire le fichier wav.
Apportons-le sous la forme " recevoir, traiter, transférer ":
- Obtenez l' audio d'un fichier wav
- Transférer l' audio vers un appareil portaudio

L'audio est lu et immédiatement lu.
Code package example import ( "github.com/dudk/phono" "github.com/dudk/phono/pipe" "github.com/dudk/phono/portaudio" "github.com/dudk/phono/wav" )
Tout d'abord, nous créons les éléments du futur pipeline: wav.Pump
et portaudio.Sink
et les passons au pipe.New
constructeur. La fonction d' p.Do(pipe.actionFn) error
démarre le pipeline et attend qu'il se termine.
Plus dur
Tâche: divisez le fichier wav en échantillons, composez-en une piste, enregistrez le résultat et jouez-le simultanément.
Une piste est une séquence d'échantillons et un échantillon est un petit segment d'audio. Pour couper l'audio, vous devez d'abord le charger en mémoire. Pour ce faire, utilisez le type asset.Asset
du asset.Asset
phono/asset
. Nous divisons la tâche en étapes standard:
- Obtenez l' audio d'un fichier wav
- Transférer l' audio en mémoire
Maintenant, nous faisons des échantillons avec nos mains, les ajoutons à la piste et terminons la tâche:
- Obtenez l' audio d'une piste
- Transférer l' audio vers
- fichier wav
- appareil portaudio

Encore une fois, sans étape de traitement, mais deux pipelines!
Code package example import ( "github.com/dudk/phono" "github.com/dudk/phono/asset" "github.com/dudk/phono/pipe" "github.com/dudk/phono/portaudio" "github.com/dudk/phono/track" "github.com/dudk/phono/wav" )
Par rapport à l'exemple précédent, il existe deux pipe.Pipe
. Le premier transfère les données en mémoire afin que vous puissiez couper les échantillons. Le second a deux destinataires à la fin: wav.Sink
et portaudio.Sink
. Avec ce schéma, le son est simultanément enregistré dans un fichier wav et lu.
Plus dur
Tâche: lire deux fichiers wav, mélanger, traiter le plugin vst2 et enregistrer dans un nouveau fichier wav.
Il y a un mixer.Mixer
simple. mixer.Mixer
dans le mixer.Mixer
phono/mixer
. Il peut transmettre des signaux de plusieurs sources et en mélanger une. Pour ce faire, il implémente simultanément pipe.Pump
et pipe.Sink
.
Encore une fois, la tâche consiste en deux sous-tâches. Le premier ressemble à ceci:
- Obtenez le fichier audio wav
- Transférez l' audio vers la console de mixage
Deuxièmement:
- Obtenez l' audio de la console de mixage.
- Traitement du plugin audio
- Transférer l' audio vers un fichier wav

Code package example import ( "github.com/dudk/phono" "github.com/dudk/phono/mixer" "github.com/dudk/phono/pipe" "github.com/dudk/phono/vst2" "github.com/dudk/phono/wav" vst2sdk "github.com/dudk/vst2" )
Il y a déjà trois pipe.Pipe
, tous reliés entre eux par un mélangeur. Pour commencer, utilisez la fonction p.Begin(pipe.actionFn) (pipe.State, error)
. Contrairement à l' p.Do(pipe.actionFn) error
, il ne bloque pas l'appel, mais renvoie simplement un état qui peut ensuite être attendu avec l' p.Wait(pipe.State) error
.
Et ensuite?
Je veux que le phono
devienne le cadre d'application le plus pratique. Si vous avez un problème avec le son, vous n'avez pas besoin de comprendre les API complexes et de passer du temps à étudier les normes. Il suffit de construire un convoyeur à partir d'éléments appropriés et de le faire fonctionner.
Pendant six mois, les packages suivants ont été filmés:
phono/wav
- lire / écrire des fichiers wavphono/vst2
- liaisons incomplètes du SDK VST2, alors que vous ne pouvez ouvrir que le plugin et appeler ses méthodes, mais pas toutes les structuresphono/mixer
- table de mixage, ajoute N signaux, sans balance ni volumephono/asset
- échantillonnage tamponphono/track
- lecture séquentielle des échantillons (superposition cassée)phono/portaudio
- lecture du signal pendant les expériences
En plus de cette liste, il y a un arriéré sans cesse croissant de nouvelles idées et idées, notamment:
- Compte à rebours
- Variable à la volée
- Pompe / évier HTTP
- Automatisation des paramètres
- Processeur de rééchantillonnage
- Balance et volume du mixeur
- Pompe en temps réel
- Pompe synchronisée pour plusieurs pistes
- Vst2 complet
Dans les articles suivants, j'analyserai:
pipe.Pipe
vie du pipe.Pipe
- en raison de la structure complexe, son état est contrôlé par l'atome final- comment écrire les étapes de votre pipeline
Ceci est mon premier projet open-source, donc je serai reconnaissant pour toute aide et recommandations. Vous êtes les bienvenus.
Les références