Přejít k navigační liště

Zdroják » JavaScript » HTML5 a přístup k mikrofonu

HTML5 a přístup k mikrofonu

Články JavaScript

Má váš počítač mikrofon? Pak k němu můžete přistupovat i z webové aplikace. Tedy, pokud jí to dovolíte. Ukážeme vám, jak na to, a můžete si to rovnou vyzkoušet.

Tento článek je překladem tutoriálu getUserMedia #2 – Microphone Access z webu Web Apprentice a jeho autorem je Rob Jones. Překlad je zde uveden s laskavým svolením autora.

Úvod

Dřívější článek HTML5, getUserMedia a práce s kamerou vám ukázal, jak můžete přistupovat k webkameře a zobrazovat její video pomocí canvasu. Tento článek naváže a představí vám, jak přistupovat k mikrofonu.

Nejjednodušším demem by bylo puštění vstupu z mikrofonu přímo do vašich reproduktorů, jenže pokud nemáte připojená sluchátka, tak by vznikla zpětná vazba. Místo toho použijeme kód z článku o vizualizaci audia a budeme náš hlasový vstup vizualizovat pomocí canvasu pomocí canvasu.

Metoda getUserMedia není zatím implementována ve všech prohlížečích, ovšem to se brzy zlepší…

V prosinci 2013, kdy vznikl tento text, byl getUserMedia podporován jen v prohlížečích Google Chrome a Mozilla Firefox. Aktuální podporu najdete na caniuse.com.

Takhle vypadá zobrazení hlasového vstupu pomocí našeho dema:

Demo 1 screenshot for this tutorial

Co se děje v kódu

Kód pro vizualizaci založíme na příkladu z článku o vizualizaci audia, kde najdete podrobnější popis, jak vizualizace funguje. V něm fungoval kód dle schématu:

Image 1 for this tutorial

Audio bylo nahráno ze souboru do SourceNode, předáno do DestinationNode (to jsou rozhraní z Web Audio API), který je přehrál na reproduktorech. Současně je převzal Analyser Node (další rozhraní z Web Audio API) Javascript Node připravil Time Domain data, která jsou třeba pro vizualizaci.

V našem případě bude schéma fungovat následovně:

Image 2 for this tutorial

Audio stream z mikrofonu předáme do Source Node, ovšem nebudeme ho propojovat přímo na Destination Node, ponecháme pouze cestu zajišťující zpracování a zobrazení dat.

V následujícím kódu sdělujeme, že getUserMedias má získat přístup k audio vstupu, ale už ne k video vstupu. Jelikož všechny prohlížeče zatím getUserMedia nepodporují, obalíme kód try / catch blokem a zobrazíme uživateli informaci o případém selhání.

        $("#start_button").click(function(e) {
            [...]
            try {
                navigator.getUserMedia(
                  { video: false,
                    audio: true},
                  setupAudioNodes,
                  onError);
            } catch (e) {
                alert('webkitGetUserMedia threw exception :' + e);
            }
        });

Metoda navigator.getUserMedia zavolá naši funkci setupAudioNodes a předá jí parametrem audio stream. Ten předáme metodě audioContext.createMediaStreamSource, abychom vytvořili sourceNode, který obsahuje přístup ke zvukovému vstupu mikrofonu.

    function setupAudioNodes(stream) {
        sourceNode = audioContext.createMediaStreamSource(stream);
        audioStream = stream;
        [...]
    }

Následuje komentovaný výpis celého kódu:

<p style="text-align: center">Click Start and begin speaking</p>
<canvas id="canvas" width="800" height="256" ></canvas>

<p id="controls">
  <input type="button" id="start_button" value="Start">
  &nbsp; &nbsp;
  <input type="button" id="stop_button" value="Stop">
</p>

<!-- ----------------------------------------------------- -->

<style>
    #canvas {
        margin-left: auto;
        margin-right: auto;
        display: block;
        background-color: black;
    }
    #controls {
        text-align: center;
    }
    #start_button, #stop_button {
        font-size: 16pt;
    }
</style>

<!-- ----------------------------------------------------- -->

<script type="text/javascript">

    // Hack pro vypořádání se s vendor prefixy
    navigator.getUserMedia = ( navigator.getUserMedia ||
                               navigator.webkitGetUserMedia ||
                               navigator.mozGetUserMedia ||
                               navigator.msGetUserMedia);

    window.requestAnimFrame = (function(){
      return  window.requestAnimationFrame       ||
              window.webkitRequestAnimationFrame ||
              window.mozRequestAnimationFrame    ||
              function(callback, element){
                window.setTimeout(callback, 1000 / 60);
              };
    })();

    window.AudioContext = (function(){
        return  window.webkitAudioContext || window.AudioContext || window.mozAudioContext;
    })();

    // Globální proměnné pro audio
    var audioContext;
    var analyserNode;
    var javascriptNode;
    var sampleSize = 1024;  // počet vzorků nutných k nasbírání pro začátek analýzy
                            // nižší hodnota = rychlejší sonogram
    var amplitudeArray;     // pole pro frekvenční data
    var audioStream;

    // Globální proměnné pro kreslení
    var column = 0;
    var canvasWidth  = 800;
    var canvasHeight = 256;
    var ctx;

    $(document).ready(function() {
        ctx = $("#canvas").get()[0].getContext("2d");

        try {
            audioContext = new AudioContext(); // rozhraní pro zpracování audia 
        } catch(e) {
            alert('Web Audio API is not supported in this browser');
        }

        // Po kliknutí na tlačítko Start dokonči nastavení audio nodů a začni
        // zpracovávat zvukový vstup ze vstupního zařízení
        $("#start_button").click(function(e) {
            e.preventDefault();
            clearCanvas();

            // získej vstupní zvukový stream a nastav nody
            try {
                navigator.getUserMedia(
                  { video: false,
                    audio: true},
                  setupAudioNodes,
                  onError);
            } catch (e) {
                alert('webkitGetUserMedia threw exception :' + e);
            }
        });

        // Ukonči zpracovávání audia
        $("#stop_button").click(function(e) {
            e.preventDefault();
            javascriptNode.onaudioprocess = null;
            if(audioStream) audioStream.stop();
            if(sourceNode)  sourceNode.disconnect();
        });
    });

    function setupAudioNodes(stream) {
        // vytvoří media stream ze zvukového vstupu (microfonu)
        sourceNode = audioContext.createMediaStreamSource(stream);
        audioStream = stream;

        analyserNode   = audioContext.createAnalyser();
        javascriptNode = audioContext.createScriptProcessor(sampleSize, 1, 1);

        // Vytvoř pole analýzu dat
        amplitudeArray = new Uint8Array(analyserNode.frequencyBinCount);

        // nastav obsluhu události, která bude vyvolána vždy po nasbírání dostatečného počtu vzorků
        // proveď analýzu a nakresli jeden sloupec do vizualizace
        javascriptNode.onaudioprocess = function () {

            amplitudeArray = new Uint8Array(analyserNode.frequencyBinCount);
            analyserNode.getByteTimeDomainData(amplitudeArray);

            // nakresli jeden sloupec
            requestAnimFrame(drawTimeDomain);
        }

        // Nyní spojíme nody dohromady
        // Nepropojujeme source node do destinace, abychom zabránili zpětné vazbě
        sourceNode.connect(analyserNode);
        analyserNode.connect(javascriptNode);
        javascriptNode.connect(audioContext.destination);
    }

    function onError(e) {
        console.log(e);
    }

    function drawTimeDomain() {
        var minValue = 9999999;
        var maxValue = 0;

        for (var i = 0; i < amplitudeArray.length; i++) {
            var value = amplitudeArray[i] / 256;
            if(value > maxValue) {
                maxValue = value;
            } else if(value < minValue) {
                minValue = value;
            }
        }

        var y_lo = canvasHeight - (canvasHeight * minValue) - 1;
        var y_hi = canvasHeight - (canvasHeight * maxValue) - 1;

        ctx.fillStyle = '#ffffff';
        ctx.fillRect(column,y_lo, 1, y_hi - y_lo);

        // smyčka přes celý canvas až dokud nenarazíme na jeho konec
        column += 1;
        if(column >= canvasWidth) {
            column = 0;
            clearCanvas();
        }
    }

    function clearCanvas() {
        column = 0;
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
        // ctx.beginPath();
        ctx.strokeStyle = '#f00';
        var y = (canvasHeight / 2) + 0.5;
        ctx.moveTo(0, y);
        ctx.lineTo(canvasWidth-1, y);
        ctx.stroke();
    }

</script>

Je to sice hodně kódu, ale jen několik řádek se týká vlastního přístupu ke zvukovému vstupu. Snad vám to pomůže při tvorbě vašeho vlastního kódu. Jedná se o úžasnou věc a jakmile bude implementována ve všech prohlížečích, určitě ji začne využívat řada nových aplikací.

Demo a zdrojový kód

  1. zdrojový kód na GitHubu
  2. funkční demo z článku

Komentáře

Odebírat
Upozornit na
guest
1 Komentář
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
Zobrazit všechny komentáře
Radek

Díky za parádní minimalistické zpracování pěkného tutoriálu.

Cesta URL: co se děje, než se načte webová stránka

Když do adresního řádku prohlížeče napíšete webovou adresu a stisknete Enter, spustí se fascinující řetězec procesů, které propojují váš počítač s celým světem. Od překladu doménového jména na IP adresu, přes navázání šifrovaného spojení, až po vykreslení každého pixelu na obrazovce - to všechno se odehraje během zlomků sekundy. Pojďme se podívat, co se mezitím děje pod kapotou webu.

Stav SIMD v Rustu v roce 2025

Různé
Komentáře: 1
SIMD - neboli Single Instruction, Multiple Data - znamená, že procesor může jednou instrukcí zpracovat více datových prvků najednou. Typicky to znamená, že místo sčítání dvou čísel přičtete dvě sady čísel paralelně. To může přinést výrazné zrychlení například při zpracování obrazu, audia nebo numerických výpočtů. Pokud již SIMD znáte, tato tabulka je vše, co budete potřebovat. A pokud s SIMD teprve začínáte, tabulku pochopíte do konce tohoto článku