Registrazione dell'audio dell'utente

Molti browser ora hanno la possibilità di accedere all'input video e audio dell'utente. Tuttavia, a seconda del browser, potrebbe trattarsi di un'esperienza completamente dinamica e in linea oppure potrebbe essere delegata a un'altra app sul dispositivo dell'utente.

Inizia con una configurazione semplice e aumenta il livello

La cosa più semplice da fare è chiedere all'utente un file preregistrato. Per farlo, crea un semplice elemento di input del file e aggiungi un filtro accept che indica che possiamo accettare solo file audio e un attributo capture che indica che vogliamo acquisirlo direttamente dal microfono.

<input type="file" accept="audio/*" capture />

Questo metodo funziona su tutte le piattaforme. Su computer, all'utente verrà chiesto di caricare un file dal file system (ignorando l'attributo capture). In Safari su iOS, si aprirà l'app del microfono, che ti consentirà di registrare l'audio e poi inviarlo alla pagina web. Su Android, l'utente potrà scegliere l'app da utilizzare per registrare l'audio prima di inviarlo alla pagina web.

Una volta che l'utente ha terminato la registrazione ed è tornato al sito web, devi in qualche modo recuperare i dati del file. Puoi ottenere un accesso rapido collegando un evento onchange all'elemento di input e poi leggendo la proprietà files dell'oggetto evento.

<input type="file" accept="audio/*" capture id="recorder" />
<audio id="player" controls></audio>
  <script>
    const recorder = document.getElementById('recorder');
    const player = document.getElementById('player');

    recorder.addEventListener('change', function (e) {
      const file = e.target.files[0];
      const url = URL.createObjectURL(file);
      // Do something with the audio file.
      player.src = url;
    });
  </script>
</audio>

Una volta ottenuto l'accesso al file, puoi farci tutto ciò che vuoi. Ad esempio, puoi:

  • Collegalo direttamente a un elemento <audio> per poterlo riprodurre
  • Scaricalo sul dispositivo dell'utente
  • Caricalo su un server allegandolo a un XMLHttpRequest
  • Passarlo attraverso l'API Web Audio e applicarvi dei filtri

Sebbene l'utilizzo del metodo dell'elemento di input per accedere ai dati audio sia molto diffuso, è l'opzione meno interessante. Vogliamo davvero ottenere l'accesso al microfono e offrire un'esperienza piacevole direttamente nella pagina.

Accedere al microfono in modo interattivo

I browser moderni possono avere una linea diretta con il microfono, consentendoci di creare esperienze completamente integrate con la pagina web e l'utente non lascerà mai il browser.

Acquisire l'accesso al microfono

Possiamo accedere direttamente al microfono utilizzando un'API nella specifica WebRTC chiamata getUserMedia(). getUserMedia() chiederà all'utente l'accesso ai microfoni e alle videocamere collegati.

Se l'operazione va a buon fine, l'API restituirà un Stream che conterrà i dati della videocamera o del microfono, che potremo poi collegare a un elemento <audio>, a uno stream WebRTC, a un AudioContext Web Audio o salvare utilizzando l'API MediaRecorder.

Per ottenere i dati dal microfono, impostiamo semplicemente audio: true nell'oggetto constraints che viene passato all'API getUserMedia().

<audio id="player" controls></audio>
<script>
  const player = document.getElementById('player');

  const handleSuccess = function (stream) {
    if (window.URL) {
      player.srcObject = stream;
    } else {
      player.src = stream;
    }
  };

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

Se vuoi scegliere un microfono specifico, puoi prima elencare i microfoni disponibili.

navigator.mediaDevices.enumerateDevices().then((devices) => {
  devices = devices.filter((d) => d.kind === 'audioinput');
});

Puoi quindi passare il deviceId che vuoi utilizzare quando chiami getUserMedia.

navigator.mediaDevices.getUserMedia({
  audio: {
    deviceId: devices[0].deviceId,
  },
});

Di per sé, questo non è molto utile. Tutto ciò che possiamo fare è prendere i dati audio e riprodurli.

Accedere ai dati non elaborati del microfono

Per accedere ai dati non elaborati del microfono, dobbiamo prendere lo stream creato da getUserMedia() e poi utilizzare l'API Web Audio per elaborare i dati. L'API Web Audio è una semplice API che accetta sorgenti di input e le collega a nodi che possono elaborare i dati audio (regolare il guadagno e così via) e, infine, a un altoparlante in modo che l'utente possa ascoltarli.

Uno dei nodi che puoi collegare è un AudioWorkletNode. Questo nodo offre la funzionalità di basso livello per l'elaborazione audio personalizzata. L'elaborazione audio effettiva avviene nel metodo di callback process() in AudioWorkletProcessor. Chiama questa funzione per fornire input e parametri e recuperare gli output.

Per saperne di più, consulta Enter Audio Worklet.

<script>
  const handleSuccess = async function(stream) {
    const context = new AudioContext();
    const source = context.createMediaStreamSource(stream);

    await context.audioWorklet.addModule("processor.js");
    const worklet = new AudioWorkletNode(context, "worklet-processor");

    source.connect(worklet);
    worklet.connect(context.destination);
  };

  navigator.mediaDevices.getUserMedia({ audio: true, video: false })
      .then(handleSuccess);
</script>
// processor.js
class WorkletProcessor extends AudioWorkletProcessor {
  process(inputs, outputs, parameters) {
    // Do something with the data, e.g. convert it to WAV
    console.log(inputs);
    return true;
  }
}

registerProcessor("worklet-processor", WorkletProcessor);

I dati contenuti nei buffer sono i dati non elaborati del microfono e hai diverse opzioni per utilizzarli:

  • Caricalo direttamente sul server
  • Archiviarli localmente
  • Convertilo in un formato file dedicato, ad esempio WAV, quindi salvalo sui tuoi server o localmente

Salvare i dati del microfono

Il modo più semplice per salvare i dati dal microfono è utilizzare l'API MediaRecorder.

L'API MediaRecorder acquisirà lo stream creato da getUserMedia e poi salverà progressivamente i dati presenti nello stream nella destinazione che preferisci.

<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
  const downloadLink = document.getElementById('download');
  const stopButton = document.getElementById('stop');


  const handleSuccess = function(stream) {
    const options = {mimeType: 'audio/webm'};
    const recordedChunks = [];
    const mediaRecorder = new MediaRecorder(stream, options);

    mediaRecorder.addEventListener('dataavailable', function(e) {
      if (e.data.size > 0) recordedChunks.push(e.data);
    });

    mediaRecorder.addEventListener('stop', function() {
      downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
      downloadLink.download = 'acetest.wav';
    });

    stopButton.addEventListener('click', function() {
      mediaRecorder.stop();
    });

    mediaRecorder.start();
  };

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

Nel nostro caso, salviamo i dati direttamente in un array che possiamo poi trasformare in un Blob, che può essere utilizzato per salvare i dati sul nostro server web o direttamente nello spazio di archiviazione sul dispositivo dell'utente.

Chiedi l'autorizzazione per utilizzare il microfono in modo responsabile

Se l'utente non ha concesso in precedenza al tuo sito l'accesso al microfono, nel momento in cui chiami getUserMedia, il browser chiederà all'utente di concedere al tuo sito l'autorizzazione per il microfono.

Gli utenti non amano ricevere richieste di accesso a dispositivi potenti sul proprio computer e spesso bloccano la richiesta o la ignorano se non comprendono il contesto in cui è stata creata. La best practice è richiedere l'accesso al microfono solo quando è necessario per la prima volta. Una volta che l'utente ha concesso l'accesso, non gli verrà più chiesto, ma se rifiuta l'accesso, non potrai più chiedergli l'autorizzazione.

Utilizza l'API Permissions per verificare se hai già l'accesso

L'API getUserMedia non ti fornisce informazioni sull'accesso al microfono. Questo ti pone un problema: per fornire una bella UI per convincere l'utente a concederti l'accesso al microfono, devi chiedere l'accesso al microfono.

In alcuni browser, questo problema può essere risolto utilizzando l'API Permission. L'API navigator.permission ti consente di eseguire query sullo stato della possibilità di accedere ad API specifiche senza dover chiedere di nuovo.

Per verificare se hai accesso al microfono dell'utente, puoi passare {name: 'microphone'} nel metodo di query, che restituirà:

  • granted: l'utente ti ha concesso l'accesso al microfono in precedenza;
  • prompt: l'utente non ti ha concesso l'accesso e gli verrà richiesto quando chiami getUserMedia;
  • denied: il sistema o l'utente ha bloccato esplicitamente l'accesso al microfono e non potrai accedervi.

Ora puoi controllare rapidamente se devi modificare l'interfaccia utente per adattarla alle azioni che l'utente deve eseguire.

navigator.permissions.query({name: 'microphone'}).then(function (result) {
  if (result.state == 'granted') {
  } else if (result.state == 'prompt') {
  } else if (result.state == 'denied') {
  }
  result.onchange = function () {};
});

Feedback