Registratore vocale digitale con ARDUINO UNO

Registratore vocale digitale con ARDUINO UNO by Andrea Paolini is licensed under CC BY 4.0

Pensando alla conversione analogico-digitale e digitale-analogica, ho avuto l’idea di trasformare il mio ARDUINO UNO in un registratore vocale digitale. L’idea di base è abbastanza semplice: nel ciclo loop(), ci sarà sicuramente qualcosa come voce = analogRead(A0); per prelevare i campioni della voce e convertirli in numeri compresi tra 0 e 1023. Infatti la funzione analogRead, converte a 10 bit; poiché 2^10 = 1024, allora i campioni saranno numeri compresi tra 0 e 1023.

Altra idea fondamentale è non inserire ritardi (delay), per non rischiare di perdere qualche campione.

Tuttavia, vi sono diversi problemi da affrontare:

  1. ARDUINO ha poca memoria. Se voglio registrare sulla memoria di ARDUINO, non vado molto lontano. Se dico “Ciao!”, forse mi registra “Ci” e poi va in crash, per aver superato la memoria disponibile.
  2. Per conservare la registrazione, devo trasferirla su una memoria non volatile.
  3. Quando voglio ascoltare la registrazione, devo usare le uscite analogiche di ARDUINO, con la funzione analogWrite. Quest’ultima, in realtà, è una uscita di tipo PWM e non una vera e propria uscita analogica. Inoltre, una uscita analogica con ARDUINO varia tra 0V e 5V e non assume valori negativi.
  4. IL microfono che ho utilizzato, genera un’uscita dell’ordine delle decine di millivolt. Se il riferimento per i segnali analogici è 5V; quindi se 1023 corrisponde a 5V, a quale valore corrisponderanno i campioni prelevati? Valori bassissimi e vicinissimi.

I problemi sopra elencati sono stati risolti o bypassati. Ecco, allora, come ho realizzato il progetto.

Realizzazione del progetto

  1. ARDUINO ha poca memoria? Allora usiamo quella del computer. Con l’istruzione Serial.println(voce), trasferisco i campioni sulla porta USB “COM3”. Uno script sul computer, scritto in javascript e che funziona con node.js, raccoglie i campioni e li salva in un file di testo (estensione .txt). Il file di testo risulterà contenere tanti numeri, compresi tra 0 e 1023.
  2. Per conservare la registrazione ho bisogno di memoria non volatile? Problema già risolto. Come appena detto, la registrazione è stata salvata in un file di testo sul computer. Questo file contiene i campioni: “543, 567, 632, 508, …” e l’informazione fondamentale sulla frequenza di campionamento utilizzata.
  3. Per riprodurre l’audio registrato non ho utilizzato analogWrite e una uscita analogica di ARDUINO. Ho convertito, attraverso una serie di passaggi, il mio file di testo, contenente i campioni, in un file wave, con estensione .wav, ascoltabile dal computer. Poi vi spiego nei dettagli questo punto cruciale.
  4. Poiché il microfono genera un’uscita dell’ordine delle decine di millivolt, per avere campioni sufficientemente grandi, ho utilizzato un riferimento analogico sul pin AREF. La tensione tra AREF e GND è stata ottenuta con un partitore resistivo.

Ecco lo schema di collegamento di ARDUINO UNO. IL microfono, da me utilizzato, ha 4 pin e non soltanto 3, come in figura. Un pin è una uscita digitale (D0), che non ho utilizzato. IL Buzzer del mio progetto ha 3 pin, non soltanto 2; Questi sono 5V, GND, e S (signal). A parte questi dettagli, il disegno rappresenta il mio progetto. Purtroppo quando disegnavo con FRITZING non avevo a disposizione i componenti realmente utilizzati e ho dovuto sceglierne altri simili.

IL LED ROSSO, ha la funzione di avvertire la persona che parla al microfono che la registrazione è terminata e la voce non viene più campionata. Durante la registrazione è spento. Quando si accende è inutile continuare a parlare. Con lo sketch da me proposto, vengono raccolti 65000 campioni in poco più di 17 secondi. La frequenza di campionamento è circa 3823 campioni/sec. Non è granché, è vero!! La registrazione non ha una grande qualità. Tuttavia, le parole che pronunciavo durante le prove risultavano ben comprensibili.

Spiegazione per immagini

Questo è il comando che lancia lo script serialport.js. L’output viene ridiretto sul file di testo che conterrà i campioni
Con il programma “conraw.exe”, scritto in C, converto i campioni acquisiti in un audio grezzo. Il file “27luglio2022-10bit.txt” è stato ottenuto dal precedente “27luglio2022.txt” dopo aver rimosso le informazioni finali sulla frequenza di campionamento (3823 campioni/secondo). Inoltre è stato aggiunto il numero 5000, alla fine dell’elenco dei campioni. IL dato “5000” serve a “conraw.exe” per riconoscere la fine dell’elenco dei campioni.
Infine, con il programma ffmpeg.exe, scaricabile gratuitamente dal sito ufficiale, converto l’audio grezzo (file .raw) in audio riproducibile al computer (file .wav). Si noti il dato “3823” passato al programma ffmpeg; “-ar 3823” significa audio rate uguale a 3823 campioni/sec.
ffmpeg “si lamenta”, ma il file viene comunque generato, e funziona.

Ecco il contenuto del file “serialport.js”…

function myconsolelog(miobuffer) {

                         console.log(miobuffer.toString());

                                                 }

const { SerialPort } = require('serialport')
const port = new SerialPort({ path: "COM3", baudRate: 2000000 });
const { DelimiterParser } = require('@serialport/parser-delimiter')
const parser = port.pipe(new DelimiterParser({ delimiter: '\n' }))
var miobuffer = parser.on('data', myconsolelog); // emits data after every '\n'


Ecco il sorgente “convraw.c” del programma “convraw.exe”


#include <stdio.h>
#include <math.h>

float x;
int y;
char z;

int main () {
  
while (x != 5000) {

            scanf("%f",&x);

            y = round((x/1023)*255);

            z = y;
       
            printf("%c",z);

             };

            return 0;

            }

Ecco il contenuto dello sketch, caricato in ARDUINO.


volatile int voce;
unsigned int i = 0;
unsigned int inizio,fine,durata,freqcamp;
bool primociclo = true;

void setup() {
  analogReference(EXTERNAL);
  Serial.begin(2000000);
  pinMode(9, OUTPUT);
  pinMode(4, OUTPUT);
             }


void loop() {
  if (primociclo){
inizio = millis();
while (i < 65000)  {  
  
if (i< 100) { tone(9,4000);};
if (i == 100) {noTone(9);};

voce = analogRead(A0); 

Serial.println(voce); i++; };

fine = millis();
 digitalWrite(4, HIGH); 

durata = fine-inizio;   // durata in millisecondo
durata = durata/1000;   // durata in secondi
freqcamp = i/durata;    // frequenza campionamento in campioni al secondo
Serial.println("durata in secondi");
Serial.println(durata);
Serial.println("frequenza campionameno in campioni/secondo");
Serial.println(freqcamp);
                };
                primociclo = false;
            }
            

Questo, invece, è un estratto del contenuto del file di testo, contenente i campioni acquisiti. L’originale è molto più lungo.

585
628
653
618
511
491
484
688
720
727
731
553
516
498
704
729
734
735
557
518
501
677
732
735
735
571
526
505
548
728
736
735
617
535
508
497
730
732
735
695
559
520
500
700
736
735
735
575
528
505
616
732
736
735
597
527
505
500
725
731
735
661
549
514
498
714
736
735
661
549
515
498
715
736
735
713
563
523
504
694
736
735
735
582
522
504
.
.
.
555
554
555
554
557
556
556
556
557
556
556
556
555
555
557
555
555
556
durata in secondi
17
frequenza campionameno in campioni/secondo
3823

Conclusione

Il progetto é sicuramente migliorabile. ARDUINO, probabilmente non é nato per applicazioni AUDIO. Il lettore consideri l’articolo come un esercizio didattico, che mostra come, anche ARDUINO, possa “diventare un registratore per l’audio”, pur con tutti i suoi limiti.

Andrea Paolini

2 Risposte a “Registratore vocale digitale con ARDUINO UNO”

  1. Prova tecnica sulla pubblicazione dei commenti.

    Ne approfitto per pubblicare un sorgente C, del programma “convraw.c”, sicuramente migliore di quello proposto nell’articolo.

    #include …

    int y;

    int main () {

    while (y != 5000) {
    scanf(“%d”,&y);
    if (y != 5000) { printf(“%c”,y);};
    };

    return 0;

    }

  2. Ecco la versione in c++

    file convraw.cpp

    #include iostream

    using namespace std;

    int y;

    int main () {

    while (y != 5000) {

    std::cin>>y;

    if (y != 5000) { std::printf(“%c”,y);};

    };

    return 0;

    }

I commenti sono chiusi.