Créa-blog

Ressources pour développeur web

API javascript MediaRecorder et Audio : Coder un dictaphone

Accueil Javascript API javascript MediaRecorder et Audio : Coder un dictaphone

Cet article explore le processus d’enregistrement et de visualisation d’audio en utilisant les API Web Audio et MediaRecorder. Nous allons décomposer un exemple de code et expliquer comment il fonctionne pour capturer l’audio du microphone, l’analyser et le visualiser, et enfin enregistrer les enregistrements sous forme de fichiers audio lisibles.

L’enregistrement et la visualisation d’audio dans le navigateur web ouvrent de nombreuses possibilités pour des applications web interactives et immersives. Cela permet aux utilisateurs d’enregistrer des notes vocales, des mémos musicaux, des commentaires audio ou tout autre type d’audio directement dans leur navigateur, sans avoir besoin d’installer de logiciel supplémentaire.

Vous devez accepter l’utilisation du micro pour tester notre Dictaphone web

Dictaphone web

Notre code utilise principalement deux API : MediaRecorder et Audio. MediaRecorder pour enregistrer des flux audio ou vidéo à partir de périphériques tels que le microphone ou la webcam de l'utilisateur. Elle est utilisée dans le code pour capturer l'audio du microphone et l'enregistrer dans des morceaux de données. Audio pour le traitement et la manipulation audio dans le navigateur. Elle est utilisée dans le code pour analyser le spectre de fréquences de l'audio en temps réel à l'aide de l'objet AnalyserNode et visualiser les données audio sous forme d'onde en utilisant le canevas et son contexte de dessin.

Nous utilisons également L'API Prompt pour demander à l'utilisateur un nom pour l'enregistrement audio et l'API Blob pour créer un objet à partir des données audio enregistrées permettant de les stocker et de les manipuler.

Fonctionnement du code pour notre Dictaphone web

  1. Vérification de la prise en charge de l'API MediaRecorder
  2. Configuration de l'API Web Audio
  3. Gestion des événements d'enregistrement et d'arrêt
  4. Enregistrement de l'audio
  5. Arrêt de l'enregistrement et sauvegarde
  6. Visualisation de l'audio
  7. Gestion des événements du clip audio

L'API MediaRecorder permet de capturer des flux audio du microphone de l'utilisateur. L'API Web Audio offre des fonctionnalités avancées pour le traitement et la manipulation audio. La combinaison des deux API permet d'enregistrer, d'analyser, de visualiser et de sauvegarder des enregistrements audio dans le navigateur.

La structure HTML de base

<div id="wrapperDemo">

    <h1>Dictaphone web</h1>

    <section class="main-controls">
        <canvas class="visualizer" height="80px"></canvas>
        <div id="buttons">
            <button class="record">Enregistrez</button>
            <button class="stop" disabled>Stop</button>
        </div>
    </section>

    <section class="sound-clips"></section>

</div>

<canvas class="visualizer" height="80px"></canvas>
L'élément canvas est utilisé pour créer des graphiques dynamique dans la page Web. Dans cette application, il est utilisé pour visualiser le signal audio (les formes d'onde) pendant l'enregistrement.

<section class="sound-clips"></section>
Cela va crée une autre section avec un attribut de classe défini sur sound-clips. Cette section sert de conteneur où les clips audio enregistrés seront affichés après l'enregistrement.

Le script en javascript avec les API MediaRecorder et Audio

Le code commence par sélectionner divers éléments HTML à l'aide de querySelector et ensuite, il désactive le bouton stop initialement car il n'y a pas d'enregistrement en cours.

// Initialisation des variables
const record = document.querySelector(".record");
const stop = document.querySelector(".stop");
const soundClips = document.querySelector(".sound-clips");
const canvas = document.querySelector(".visualizer");
const mainSection = document.querySelector(".main-controls");

// Désactiver le bouton d'arrêt lorsque vous n'enregistrez pas
stop.disabled = true;

Deux variables sont déclarées :

  • audioCtx : Elle contiendra la référence à l'objet AudioContext, qui est utilisé pour le traitement audio dans le navigateur.
  • canvasCtx : Elle fait référence au contexte 2D de l'élément canvas, utilisé pour dessiner la visualisation.
// Configuration du visualiseur : créer un contexte et un canevas pour l'API audio Web
let audioCtx;
const canvasCtx = canvas.getContext("2d");

Vérification de la prise en charge des périphériques multimédias

Le code vérifie si le navigateur prend en charge l'API navigator.mediaDevices.getUserMedia pour accéder au microphone de l'utilisateur. Si elle est prise en charge, il passe aux étapes suivantes :

// Bloc principal pour faire l'enregistrement audio
if (navigator.mediaDevices.getUserMedia) { ...

Accès au microphone

La fonction navigator.mediaDevices.getUserMedia est appelée, demandant l'accès au microphone de l'utilisateur. Deux fonctions de rappel sont fournies :

  • onSuccess : Cette fonction est appelée si l'utilisateur accorde l'accès au microphone.
  • onError : Cette fonction est appelée en cas d'erreur d'accès au microphone.

Logique d'enregistrement onSuccess

À l'intérieur de la fonction onSuccess :

  • Un objet MediaRecorder est créé pour enregistrer le flux audio du microphone.
  • La fonction visualize est appelée pour démarrer la visualisation audio.

Lorsque le bouton record est cliqué :

  • La méthode MediaRecorder.start() est appelée pour commencer l'enregistrement.
  • L'état d'enregistrement est enregistré dans la console.
  • La couleur de fond du bouton passe au rouge pour indiquer l'enregistrement.
  • Le bouton stop est activé et le bouton record est désactivé.

Lorsque le bouton stop est cliqué :

  • La méthode MediaRecorder.stop() est appelée pour arrêter l'enregistrement.
  • L'état d'enregistrement est enregistré dans la console.
  • Les couleurs des boutons sont réinitialisées.
  • Les deux boutons sont désactivés/activés en conséquence.

Enregistrement de l'enregistrement

L'événement MediaRecorder.ondataavailable est utilisé pour capturer des morceaux d'audio pendant l'enregistrement. Ces morceaux sont stockés dans le tableau chunks.

À l'intérieur de l'événement MediaRecorder.onstop (appelé après l'arrêt de l'enregistrement) :

  • L'utilisateur est invité à entrer un nom pour l'enregistrement (en utilisant prompt).
  • Un élément article est créé pour contenir les informations du clip enregistré.
  • Des éléments pour le libellé du clip, le lecteur audio et le bouton de suppression sont créés et ajoutés à l'article.
  • En fonction du nom saisi, le libellé du clip est défini.
  • L'audio enregistré est converti en un objet Blob, qui représente les données audio.
  • Une URL pour l'objet Blob est créée à l'aide de window.URL.createObjectURL.
  • L'attribut src du lecteur audio est défini sur l'URL, rendant l'enregistrement lisible.
  • La fonction deleteButton.onclick est définie pour supprimer l'élément du clip lorsqu'il est cliqué.
  • La fonction clipLabel.onclick est définie pour permettre de renommer le clip à l'aide de prompt.
let onSuccess = function (stream) {

    const mediaRecorder = new MediaRecorder(stream);
    visualize(stream);

    record.onclick = function () {
        mediaRecorder.start();
        console.log(mediaRecorder.state);
        console.log("Début de l'enregistrement");
        record.style.background = "red";

        stop.disabled = false;
        record.disabled = true;
    }; 

    stop.onclick = function () {
        mediaRecorder.stop();
        console.log(mediaRecorder.state);
        console.log("Fin de l'enregistrement");
        record.style.background = "";
        record.style.color = "";

        stop.disabled = true;
        record.disabled = false;
    };


    mediaRecorder.onstop = function (e) {
        console.log("Dernières données à lire (après l'appel de MediaRecorder.stop()).");

        const clipName = prompt(
        "Saisir un nom pour votre enregistrement audio ?",
        "Enregistrement sans nom"
        );

        const clipContainer = document.createElement("article");
        const clipLabel = document.createElement("p");
        const audio = document.createElement("audio");
        const deleteButton = document.createElement("button");

        clipContainer.classList.add("clip");
        audio.setAttribute("controls", "");
        deleteButton.textContent = "Supprimer";
        deleteButton.className = "delete";

        if (clipName === null) {
            clipLabel.textContent = "Enregistrement sans nom";
        } else {
            clipLabel.textContent = clipName;
        }

        clipContainer.appendChild(audio);
        clipContainer.appendChild(clipLabel);
        clipContainer.appendChild(deleteButton);
        soundClips.appendChild(clipContainer);

        audio.controls = true;
        const blob = new Blob(chunks, { type: mediaRecorder.mimeType });
        chunks = [];
        const audioURL = window.URL.createObjectURL(blob);
        audio.src = audioURL;
        console.log("Fin de l'enregistrement");

        deleteButton.onclick = function (e) {
            e.target.closest(".clip").remove();
        };

        clipLabel.onclick = function () {
            const existingName = clipLabel.textContent;
            const newClipName = prompt("Entrez un nouveau nom pour l'enregistrement ?");
            if (newClipName === null) {
                clipLabel.textContent = existingName;
            } else {
                clipLabel.textContent = newClipName;
            }
        };
    };

    mediaRecorder.ondataavailable = function (e) {
        chunks.push(e.data);
    };
};

Visualisation audio - fonction visualize

Si l'objet audioCtx n'est pas encore créé, un nouvel objet AudioContext est créé pour gérer le traitement audio.

Une source audio est créée à l'aide de audioCtx.createMediaStreamSource pour connecter le flux du microphone au pipeline de traitement audio.

Un AnalyserNode est créé à l'aide de audioCtx.createAnalyser. Ce nœud aide à analyser le spectre de fréquences des flux audio.

function visualize(stream) {

    if (!audioCtx) {
        audioCtx = new AudioContext();
    }

    const source = audioCtx.createMediaStreamSource(stream);

    const analyser = audioCtx.createAnalyser();
    analyser.fftSize = 2048;
    const bufferLength = analyser.frequencyBinCount;
    const dataArray = new Uint8Array(bufferLength);

    source.connect(analyser);

    draw();

    function draw() {

        const WIDTH = canvas.width;
        const HEIGHT = canvas.height;

        requestAnimationFrame(draw);

        analyser.getByteTimeDomainData(dataArray);

        canvasCtx.fillStyle = "#f1f1f1";
        canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

        canvasCtx.lineWidth = 2;
        canvasCtx.strokeStyle = "#0077cc";

        canvasCtx.beginPath();

        let sliceWidth = (WIDTH * 1.0) / bufferLength;
        let x = 0;

        for (let i = 0; i < bufferLength; i++) {

            let v = dataArray[i] / 128.0;
            let y = (v * HEIGHT) / 2;

            if (i === 0) {
                canvasCtx.moveTo(x, y);
            } else {
                canvasCtx.lineTo(x, y);
            }

            x += sliceWidth;
        }

        canvasCtx.lineTo(canvas.width, canvas.height / 2);
        canvasCtx.stroke();
    } 
}

Script javascript complet

// Initialisation des variables
const record = document.querySelector(".record");
const stop = document.querySelector(".stop");
const soundClips = document.querySelector(".sound-clips");
const canvas = document.querySelector(".visualizer");
const mainSection = document.querySelector(".main-controls");

// Désactiver le bouton d'arrêt lorsque vous n'enregistrez pas
stop.disabled = true;

// Configuration du visualiseur : créer un contexte et un canevas pour l'API audio Web
let audioCtx;
const canvasCtx = canvas.getContext("2d");     
            
// Bloc principal pour faire l'enregistrement audio
if (navigator.mediaDevices.getUserMedia) {
                
console.log("La méthode mediaDevices.getUserMedia() est prise en charge.");

const constraints = { audio: true };
let chunks = [];

let onSuccess = function (stream) {

    const mediaRecorder = new MediaRecorder(stream);
    visualize(stream);

    record.onclick = function () {
        mediaRecorder.start();
        console.log(mediaRecorder.state);
        console.log("Début de l'enregistrement");
        record.style.background = "red";

        stop.disabled = false;
        record.disabled = true;
    }; 

    stop.onclick = function () {
        mediaRecorder.stop();
        console.log(mediaRecorder.state);
        console.log("Fin de l'enregistrement");
        record.style.background = "";
        record.style.color = "";

        stop.disabled = true;
        record.disabled = false;
    };


    mediaRecorder.onstop = function (e) {
        console.log("Dernières données à lire (après l'appel de MediaRecorder.stop()).");

        const clipName = prompt(
        "Saisir un nom pour votre enregistrement audio ?",
        "Enregistrement sans nom"
        );

        const clipContainer = document.createElement("article");
        const clipLabel = document.createElement("p");
        const audio = document.createElement("audio");
        const deleteButton = document.createElement("button");

        clipContainer.classList.add("clip");
        audio.setAttribute("controls", "");
        deleteButton.textContent = "Supprimer";
        deleteButton.className = "delete";

        if (clipName === null) {
            clipLabel.textContent = "Enregistrement sans nom";
        } else {
            clipLabel.textContent = clipName;
        }

        clipContainer.appendChild(audio);
        clipContainer.appendChild(clipLabel);
        clipContainer.appendChild(deleteButton);
        soundClips.appendChild(clipContainer);

        audio.controls = true;
        const blob = new Blob(chunks, { type: mediaRecorder.mimeType });
        chunks = [];
        const audioURL = window.URL.createObjectURL(blob);
        audio.src = audioURL;
        console.log("Fin de l'enregistrement");

        deleteButton.onclick = function (e) {
            e.target.closest(".clip").remove();
        };

        clipLabel.onclick = function () {
            const existingName = clipLabel.textContent;
            const newClipName = prompt("Entrez un nouveau nom pour l'enregistrement ?");
            if (newClipName === null) {
                clipLabel.textContent = existingName;
            } else {
                clipLabel.textContent = newClipName;
            }
        };
    };

    mediaRecorder.ondataavailable = function (e) {
        chunks.push(e.data);
    };
};

    let onError = function (err) {
        console.log("Erreur : " + err);
    };

    navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);

} else {
    console.log("MediaDevices.getUserMedia() n'est pas pris en charge sur votre navigateur !");
}

            
function visualize(stream) {

    if (!audioCtx) {
        audioCtx = new AudioContext();
    }

    const source = audioCtx.createMediaStreamSource(stream);

    const analyser = audioCtx.createAnalyser();
    analyser.fftSize = 2048;
    const bufferLength = analyser.frequencyBinCount;
    const dataArray = new Uint8Array(bufferLength);

    source.connect(analyser);

    draw();

    function draw() {

        const WIDTH = canvas.width;
        const HEIGHT = canvas.height;

        requestAnimationFrame(draw);

        analyser.getByteTimeDomainData(dataArray);

        canvasCtx.fillStyle = "#f1f1f1";
        canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

        canvasCtx.lineWidth = 2;
        canvasCtx.strokeStyle = "#0077cc";

        canvasCtx.beginPath();

        let sliceWidth = (WIDTH * 1.0) / bufferLength;
        let x = 0;

        for (let i = 0; i < bufferLength; i++) {

            let v = dataArray[i] / 128.0;
            let y = (v * HEIGHT) / 2;

            if (i === 0) {
                canvasCtx.moveTo(x, y);
            } else {
                canvasCtx.lineTo(x, y);
            }

            x += sliceWidth;
        }

        canvasCtx.lineTo(canvas.width, canvas.height / 2);
        canvasCtx.stroke();
    } 
}

window.onresize = function () {
    canvas.width = mainSection.offsetWidth;
};

window.onresize();

Pour résumer

L'exemple de code présenté démontre les capacités puissantes des API Web Audio et MediaRecorder pour enregistrer, analyser, visualiser et sauvegarder des enregistrements audio dans le navigateur web. Cette technologie ouvre de vastes possibilités pour des applications web interactives et engageantes, telles que des dictaphones, des éditeurs audio simples, des outils de création musicale ou des plateformes de partage d'audio.

Points clés

  • L'API MediaRecorder permet de capturer des flux audio du microphone de l'utilisateur.
  • L'API Web Audio offre des fonctionnalités avancées pour le traitement et la manipulation audio.
  • La combinaison des deux API permet d'enregistrer, d'analyser, de visualiser et de sauvegarder des enregistrements audio dans le navigateur.