Maîtriser la reconnaissance et la synthèse vocale sur Arduino Due
Composants et fournitures
| Composant | Quantité |
|---|---|
| Arduino Due | 1 |
| Module microphone électret SparkFun | 1 |
| Amplificateur audio mono SparkFun | 1 |
| Haut-parleur : 0.25W, 8 ohms | 1 |
| Plaque d'essai (breadboard) | 1 |
| LED 5 mm rouge | 3 |
| Résistance 330 ohms | 3 |
| Fils de connexion (jumper wires) | 1 |
Outils et machines nécessaires
- Fer à souder (générique)
Applications et services en ligne
- BitVoicer Server 1.0
À propos de ce projet
Dans mon projet précédent, j'ai montré comment contrôler quelques LEDs avec une carte Arduino et BitVoicer Server. Dans celui-ci, je complexifie un peu les choses. Je vais aussi synthétiser la parole en utilisant le convertisseur numérique-analogique (DAC) de l'Arduino DUE. Si vous n'avez pas d'Arduino DUE, vous pouvez utiliser d'autres cartes Arduino, mais vous aurez besoin d'un DAC externe et de code supplémentaire pour le piloter (la bibliothèque BVSSpeaker ne vous aidera pas pour cela).
Dans la vidéo ci-dessous, vous verrez que je fais aussi jouer une petite chanson à l'Arduino et clignoter les LEDs comme des touches de piano. Désolé pour mes compétences au piano, mais c'est le mieux que je puisse faire :) . Les LEDs clignotent en fait dans la même séquence et au même timing que de vraies touches C, D et E. Si vous avez un piano, vous pouvez suivre les LEDs et jouer la même chanson. C'est un jingle d'un ancien détaillant (Mappin) qui n'existe même plus.
Les procédures suivantes transforment les commandes vocales en activité des LEDs et parole synthétisée :
- Les ondes audio sont capturées et amplifiées par la carte Sparkfun Electret Breakout ;
- Le signal amplifié est numérisé et mis en tampon dans l'Arduino via son convertisseur analogique-numérique (ADC) ;
- Les échantillons audio sont transmis au serveur BitVoicer via le port série de l'Arduino ;
- Le serveur BitVoicer traite le flux audio et reconnaît la parole qu'il contient ;
- La parole reconnue est mappée à des commandes prédéfinies renvoyées à l'Arduino. Si une commande consiste à synthétiser la parole, le serveur prépare le flux audio et l'envoie à l'Arduino ;
- L'Arduino identifie les commandes et effectue l'action appropriée. Si un flux audio est reçu, il est mis en file d'attente dans la classe BVSSpeaker et lu via le DAC et DMA du DUE ;
- L'amplificateur audio mono SparkFun amplifie le signal du DAC pour piloter un haut-parleur 8 Ohms.
Liste du matériel
- Arduino DUE : ~50 $US
- Sparkfun Electret Microphone Breakout : 7,95 $US
- SparkFun Mono Audio Amp Breakout : 7,95 $US
- BitVoicer Server 1.0 : 9,90 $US
- Haut-parleur 8 Ohms : ~2 $US
- Breadboard : ~10 $US
- 3 LEDs : ~1 $US
- 3 résistances 330 Ohms : ~0,75 $US
- Fils de connexion : ~0,50 $US
ÉTAPE 1 : Câblage
Première étape : câbler l'Arduino et la breadboard avec les composants comme montré dans les images ci-dessous. J'ai dû placer un petit caoutchouc sous le haut-parleur car il vibre beaucoup et sans le caoutchouc, la qualité audio est considérablement affectée.
Voici une différence petite mais importante par rapport à mon projet précédent. La plupart des cartes Arduino fonctionnent à 5V, mais le DUE fonctionne à 3.3V. Comme j'ai obtenu de meilleurs résultats en alimentant le Sparkfun Electret Breakout à 3.3V, je recommande d'ajouter un cavalier entre la broche 3.3V et la broche AREF SI vous utilisez des cartes Arduino 5V. Le DUE utilise déjà une référence analogique 3.3V donc vous n'avez pas besoin de cavalier vers AREF. En fait, la broche AREF sur le DUE est connectée au microcontrôleur via un pont de résistances. Pour utiliser la broche AREF, la résistance BR1 doit être dessoudée du PCB.
ÉTAPE 2 : Téléversement du code sur l'Arduino
Maintenant, téléversez le code ci-dessous sur votre Arduino. Pour plus de commodité, le sketch Arduino est aussi disponible dans la section Pièces jointes en bas de cette page. Avant de téléverser, vous devez installer correctement les bibliothèques BitVoicer Server dans l'IDE Arduino (Importation d'une bibliothèque .zip).
Sketch Arduino : BVS_Demo2.ino
Ce sketch comporte sept parties principales :
- Références de bibliothèques et déclaration de variables : Les quatre premières lignes incluent les références aux bibliothèques BVSP, BVSMic, BVSSpeaker et DAC. Ces bibliothèques sont fournies par BitSophia et se trouvent dans le dossier d'installation de BitVoicer Server. La bibliothèque DAC est incluse automatiquement quand vous ajoutez la référence à BVSSpeaker. Les autres lignes déclarent les constantes et variables utilisées. La classe BVSP communique avec BitVoicer Server, BVSMic capture et stocke les échantillons audio, BVSSpeaker reproduit l'audio via le DAC du DUE.
- Fonction setup() : Configure les modes de broches et leur état initial ; initialise la communication série ; initialise les classes BVSP, BVSMic et BVSSpeaker. Définit aussi les « gestionnaires d'événements » (pointeurs de fonction) pour les événements frameReceived, modeChanged et streamReceived de la classe BVSP.
- Fonction loop() : Effectue cinq actions importantes : demande les infos de statut au serveur (keepAlive()) ; vérifie si le serveur a envoyé des données et traite les données reçues (receive()) ; contrôle l'enregistrement et l'envoi des flux audio (isSREAvailable(), startRecording(), stopRecording(), sendStream()) ; lit les échantillons audio mis en file dans BVSSpeaker (play()) ; appelle playNextLEDNote() qui contrôle le clignotement des LEDs après la commande playLEDNotes.
- Fonction BVSP_frameReceived : Appelée à chaque fois que receive() identifie une trame complète reçue. Exécute les commandes envoyées depuis BitVoicer Server. Les commandes contrôlant les LEDs contiennent 2 octets. Le premier indique la broche, le second la valeur. J'utilise analogWrite() pour définir la valeur appropriée. Je vérifie aussi si la commande playLEDNotes (type Byte) a été reçue. Si oui, je mets playLEDNotes à true et marque l'heure actuelle. Ce temps sera utilisé par playNextLEDNote pour synchroniser les LEDs avec la chanson.
- Fonction BVSP_modeChanged : Appelée quand receive() détecte un changement de mode dans la direction sortante (Serveur → Arduino). BitVoicer Server peut envoyer des données en trames ou des flux audio. Avant de passer d'un mode à l'autre, le serveur envoie un signal. La classe BVSP identifie ce signal et déclenche l'événement modeChanged. Dans cette fonction, si je détecte que la communication passe du mode flux au mode trame, je sais que l'audio s'est terminé et je dis à BVSSpeaker d'arrêter de lire les échantillons audio.
- Fonction BVSP_streamReceived : Appelée quand receive() identifie que des échantillons audio ont été reçus. Je récupère simplement les échantillons et les mets en file dans BVSSpeaker pour que play() les reproduise.
- Fonction playNextLEDNote : Ne s'exécute que si BVSP_frameReceived identifie la commande playLEDNotes. Contrôle et synchronise les LEDs avec l'audio envoyé depuis BitVoicer Server. Pour synchroniser les LEDs avec l'audio et connaître le bon timing, j'ai utilisé Sonic Visualizer. Ce logiciel gratuit m'a permis de voir les ondes audio pour savoir facilement quand une touche de piano était pressée. Il affiche aussi une timeline et c'est ainsi que j'ai obtenu les millisecondes utilisées dans cette fonction. Cela ressemble à une astuce farfelue et c'en est une. Je pense qu'il serait possible d'analyser le flux audio et d'allumer la LED correspondante, mais cela dépasse mes compétences.
ÉTAPE 3 : Importation des objets solution BitVoicer Server
Maintenant, configurez BitVoicer Server pour qu'il fonctionne avec l'Arduino. BitVoicer Server a quatre objets solution majeurs : Locations, Devices, BinaryData et Voice Schemas.
- Locations : Représentent l'emplacement physique où un appareil est installé. J'ai créé une location appelée Home.
- Devices : Sont les clients BitVoicer Server. J'ai créé un appareil Mixed, nommé ArduinoDUE et entré les paramètres de communication. IMPORTANT : même l'Arduino DUE a peu de mémoire pour stocker tous les échantillons audio que BitVoicer Server va streamer. Si vous ne limitez pas la bande passante, vous aurez besoin d'un tampon bien plus grand. J'ai eu des débordements de tampon pour cette raison, j'ai donc dû limiter le Data Rate dans les paramètres de communication à 8000 échantillons par seconde.
- BinaryData : Type de commande que BitVoicer Server peut envoyer aux appareils clients. Ce sont en fait des tableaux d'octets liés à des commandes. Quand le serveur reconnaît de la parole liée à une commande, il envoie le tableau d'octets à l'appareil cible. J'ai créé un objet BinaryData pour chaque valeur de broche : ArduinoDUEGreenLedOn, ArduinoDUEGreenLedOff, etc. J'ai fini avec 18 objets BinaryData dans ma solution. Je vous suggère de les télécharger et importer depuis le fichier VoiceSchema.sof ci-dessous.
- Voice Schemas : C'est là que tout se combine. Ils définissent quelles phrases doivent être reconnues et quelles commandes exécuter. Pour chaque phrase, vous pouvez définir autant de commandes que nécessaire et leur ordre d'exécution. Vous pouvez aussi définir des délais entre commandes. C'est ainsi que j'ai géré la séquence d'actions visible dans la vidéo.
Une des phrases de mon Voice Schema est « play a little song ». Cette phrase contient deux commandes. La première envoie un octet indiquant que la commande suivante sera un flux audio. L'Arduino commence alors à « jouer » les LEDs pendant que l'audio est transmis. L'audio est un petit jingle piano que j'ai enregistré moi-même et défini comme source audio de la seconde commande. BitVoicer Server ne supporte que l'audio PCM mono 8 bits (8000 échantillons/seconde). Si vous devez convertir un fichier audio dans ce format, je recommande cet outil en ligne : https://audio.online-convert.com/convert-to-wav.
Vous pouvez importer (Importation d'objets solution) tous les objets solution utilisés dans ce projet depuis les fichiers ci-dessous. L'un contient l'appareil DUE, l'autre le Voice Schema et ses commandes.
Fichiers d'objets solution :
- Device.sof
- VoiceSchema.sof
ÉTAPE 4 : Conclusion
Voilà ! Vous pouvez tout activer et faire les mêmes choses montrées dans la vidéo.
Comme dans mon projet précédent, j'ai démarré la reconnaissance vocale en activant l'appareil Arduino dans le gestionnaire BitVoicer Server. Dès qu'il est activé, l'Arduino identifie un moteur de reconnaissance vocale disponible et commence à streamer l'audio vers BitVoicer Server. Cependant, maintenant vous voyez beaucoup plus d'activité sur la LED RX de l'Arduino pendant que l'audio est streamé depuis BitVoicer Server vers l'Arduino.
Dans mon prochain projet, je serai un peu plus ambitieux. J'ajouterai la communication WiFi à un Arduino et contrôlerai deux autres Arduinos ensemble par la voix. Je pense à un genre de jeu entre eux. Les suggestions sont les bienvenues !
Code
Sketch Arduino (Arduino)
#include #include #include #include // Broche Arduino pour capture audio #define BVSM_AUDIO_INPUT 7 // Broches LED #define RED_LED_PIN 6 #define YELLOW_LED_PIN 9 #define GREEN_LED_PIN 10 // Constantes pour BVSP.begin const unsigned long STATUS_REQUEST_TIMEOUT = 3000; const unsigned long STATUS_REQUEST_INTERVAL = 4000; // Taille tampon audio micro const int MIC_BUFFER_SIZE = 64; // Taille tampon audio haut-parleur const int SPEAKER_BUFFER_SIZE = 128; // Taille tampon réception const int RECEIVE_BUFFER_SIZE = 2; // Instance globale BVSP BVSP bvsp = BVSP(); // Instance globale BVSMic BVSMic bvsm = BVSMic(); // Instance globale BVSSpeaker BVSSpeaker bvss = BVSSpeaker(); // Tampon pour lire échantillons enregistrés depuis BVSMic byte micBuffer[MIC_BUFFER_SIZE]; // Tampon pour écrire échantillons audio dans BVSSpeaker byte speakerBuffer[SPEAKER_BUFFER_SIZE]; // Tampon pour lire commandes envoyées depuis BitVoicer Server // Octet 0 = numéro broche // Octet 1 = valeur broche byte receiveBuffer[RECEIVE_BUFFER_SIZE]; // Variables pour contrôler quand jouer les "LED Notes" bool playLEDNotes = false; unsigned int playStartTime = 0; void setup() { // Modes broches pinMode(RED_LED_PIN, OUTPUT); pinMode(YELLOW_LED_PIN, OUTPUT); pinMode(GREEN_LED_PIN, OUTPUT); // État initial LEDs digitalWrite(RED_LED_PIN, LOW); digitalWrite(YELLOW_LED_PIN, LOW); digitalWrite(GREEN_LED_PIN, LOW); // Communication série à 115200 bps Serial.begin(115200); // Port série Arduino, timeout requête statut, intervalle requêtes statut bvsp.begin(Serial, STATUS_REQUEST_TIMEOUT, STATUS_REQUEST_INTERVAL); // Gestionnaire événement frameReceived bvsp.frameReceived = BVSP_frameReceived; // Gestionnaire événement modeChanged bvsp.modeChanged = BVSP_modeChanged; // Gestionnaire événement streamReceived bvsp.streamReceived = BVSP_streamReceived; // Préparer timer classe BVSMic bvsm.begin(); // Définir DAC utilisé par BVSSpeaker bvss.begin(DAC); } void loop() { // Vérifier intervalle requête statut et envoyer si écoulé bvsp.keepAlive(); // Vérifier données disponibles port série et traiter selon protocole BitVoicer Server bvsp.receive(); // Vérifier si un SRE disponible. Si oui, commencer enregistrement. if (bvsp.isSREAvailable()) { // Si BVSMic n'enregistre pas, configurer entrée audio et commencer if (!bvsm.isRecording) { bvsm.setAudioInput(BVSM_AUDIO_INPUT, DEFAULT); bvsm.startRecording(); } // Vérifier si BVSMic a échantillons disponibles if (bvsm.available) { // S'assurer que mode entrant est STREAM_MODE avant transmission if (bvsp.inboundMode == FRAMED_MODE) bvsp.setInboundMode(STREAM_MODE); // Lire échantillons audio depuis BVSMic int bytesRead = bvsm.read(micBuffer, MIC_BUFFER_SIZE); // Envoyer flux audio vers BitVoicer Server bvsp.sendStream(micBuffer, bytesRead); } } else { // Aucun SRE disponible. Si BVSMic enregistre, l'arrêter. if (bvsm.isRecording) bvsm.stopRecording(); } // Lire tous échantillons audio disponibles dans tampon interne BVSSpeaker. // Écrits dans gestionnaire d'événement BVSP_streamReceived. // Si aucun échantillon disponible, rien n'est joué. bvss.play(); // Si playLEDNotes mis à true, jouer les "notes LED" avec la musique. if (playLEDNotes) playNextLEDNote(); } // Gestionnaire événement frameReceived void BVSP_frameReceived(byte dataType, int payloadSize) { // Vérifier si trame contient données binaires // 0x07 = Données binaires (tableau octets) if (dataType == DATA_TYPE_BINARY) { // Si 2 octets reçus, traiter commande. if (bvsp.getReceivedBytes(receiveBuffer, RECEIVE_BUFFER_SIZE) == RECEIVE_BUFFER_SIZE) { analogWrite(receiveBuffer[0], receiveBuffer[1]); } } // Vérifier si trame contient données type octet // 0x01 = Type données octet else if (dataType == DATA_TYPE_BYTE) { // Si octet reçu vaut 255, activer playLEDNotes et marquer temps. if (bvsp.getReceivedByte() == 255) { playLEDNotes = true; playStartTime = millis(); } } } // Gestionnaire événement modeChanged void BVSP_modeChanged() { // Si outboundMode (Serveur -> Device) passe à FRAMED_MODE, aucun flux audio n'est censé être reçu. // Dire à BVSSpeaker de finir la lecture quand son tampon interne devient vide. if (bvsp.outboundMode == FRAMED_MODE) bvss.finishPlaying(); } // Gestionnaire événement streamReceived void BVSP_streamReceived(int size) { // Récupérer flux reçu depuis classe BVSP int bytesRead = bvsp.getReceivedStream(speakerBuffer, SPEAKER_BUFFER_SIZE); // Mettre en file pour lecture bvss.enqueue(speakerBuffer, bytesRead); } // Allumer LED appropriée selon temps depuis commande playLEDNotes. // Timings synchronisés avec la musique. void playNextLEDNote() { // Temps écoulé entre playStartTime et maintenant. unsigned long elapsed = millis() - playStartTime; // Éteindre toutes LEDs allLEDsOff(); // Dernière note jouée. Éteindre dernière LED et arrêter notes LED. if (elapsed >= 11500) { analogWrite(RED_LED_PIN, 0); playLEDNotes = false; } else if (elapsed >= 9900) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 9370) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 8900) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 8610) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 8230) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 7970) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 7470) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 6760) analogWrite(GREEN_LED_PIN, 255); // Note E else if (elapsed >= 6350) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 5880) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 5560) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 5180) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 4890) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 4420) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 3810) analogWrite(GREEN_LED_PIN, 255); // Note E else if (elapsed >= 3420) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 2930) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 2560) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 2200) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 1930) analogWrite(YELLOW_LED_PIN, 255); // Note D else if (elapsed >= 1470) analogWrite(RED_LED_PIN, 255); // Note C else if (elapsed >= 1000) analogWrite(GREEN_LED_PIN, 255); // Note E } // Éteindre toutes LEDs. void allLEDsOff() { analogWrite(RED_LED_PIN, 0); analogWrite(YELLOW_LED_PIN, 0); analogWrite(GREEN_LED_PIN, 0); }
Schéma de câblage
Processus de fabrication
- Système de pointage RFID fiable avec Arduino et Python (base de données phpMyAdmin)
- Projet capteur DHT11 température/humidité avec indicateurs LED et buzzer piézo
- Affichage LED Interactif Gyroscopique avec Anneau NeoPixel et Arduino
- Moniteur de Température et Horloge Temps Réel Arduino avec Écran TFT 3.2"
- Contrôlez votre Roomba Create 2 avec Arduino et Android – Guide pas à pas
- Construire un voltmètre DIY avec Arduino et écran Nokia 5110
- Arduino et MPU6050 : Contrôle précis de servo-moteur avec SG90 et MG996R
- Module GPS u-blox LEA‑6H 02 : guide d'intégration Arduino et Python
- Lire température et humidité avec DHT11 sur Blynk – Tutoriel Arduino pas à pas