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ší Nejvíce hlasů
Radek

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

theme.json není jen konfigurace. Je to API pro design systému WordPressu

Soubor theme.json se často bere jako „ten JSON, kam se píšou barvy a velikosti písma“. Jenže kdo s ním pracuje déle, zjistí, že jde o něco zásadnějšího. Je to veřejné, verzované a vrstvené rozhraní, přes které spolu komunikuje téma, editor, pluginy i samotný uživatel. Když ho tak začnete chápat, změní se i to, jak navrhujete a udržujete šablony.

„Hazardují s našimi životy“: Odchod výzkumníka rozjel na X debatu o vyhubení lidstva AI

AI
Komentáře: 0
Debata o tom, zda umělá inteligence může přivodit konec lidstva, se přehnala sociální sítí X. Odchod bezpečnostního výzkumníka z Anthropicu a jeho odhad desetiprocentní pravděpodobnosti vyhynutí spustil vlnu, do níž se zapojil i šéf firmy Dario Amodei. Zatímco jedni volají po zpomalení vývoje, čerstvý report ukazuje, jak jsou modely zneužívány už dnes.