{"id":1669,"date":"2012-10-28T04:08:26","date_gmt":"2012-10-28T09:08:26","guid":{"rendered":"http:\/\/www.phpied.com\/?p=1669"},"modified":"2012-10-28T04:20:46","modified_gmt":"2012-10-28T09:20:46","slug":"webaudio-live-input","status":"publish","type":"post","link":"https:\/\/www.phpied.com\/webaudio-live-input\/","title":{"rendered":"WebAudio: live input"},"content":{"rendered":"<p>Live input, aka <code>getUserMedia<\/code>: it exists in Chrome Canary for audio too. Great times to be a web developer, right?<\/p>\n<p>Let's check it out.<\/p>\n<p><a href=\"https:\/\/www.phpied.com\/files\/webaudio\/getusermedia.html\">Here's the demo<\/a>, but first a prerequisite: go <a href=\"chrome:\/\/flags\">chrome:\/\/flags<\/a>, search for \"Web Audio Input\" and enable it. Restart Chrome Canary.<\/p>\n<h3>With a guitar<\/h3>\n<p>I wanted to have a little trickier setup and capture guitar sound not just voice with a microphone.<\/p>\n<p>As always, it was bigger hurdle to get guitar sound to the computer, than anything else JavaScript-wise.<\/p>\n<p>I have a guitar amp that has a mini-USB out. This goes to the USB of the computer. Wrestle, system settings, garage band to the rescue.... eventually the computer makes sound. <\/p>\n<h3>Capturing<\/h3>\n<p>I was assuming the stream you get from <code>getuserMedia<\/code> can go directly to an HTML <code>&lt;audio&gt; src<\/code>. No such luck. <a href=\"https:\/\/www.phpied.com\/canvas-pixels-3-getusermedia\/\">Works for video<\/a> but <a href=\"http:\/\/code.google.com\/p\/chromium\/issues\/detail?id=112367\">not yet for audio<\/a>.<\/p>\n<p>So... WebAudio API saves the day.<\/p>\n<p>Setting up audio context (like in the previous post), shimming getUserMedia and setting up callbacks for it:<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-code\">  <\/span><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> for logging<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-reserved\">function<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">fire<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">e<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-identifier\">data<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">    \r\n    <\/span><span class=\"hl-identifier\">log<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">innerHTML<\/span><span class=\"hl-code\"> += <\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-special\">\\n<\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-code\"> + <\/span><span class=\"hl-identifier\">e<\/span><span class=\"hl-code\"> + <\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-string\"> <\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-code\"> + <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">data<\/span><span class=\"hl-code\"> || <\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n  <\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-code\">\r\n \r\n  <\/span><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> globals<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-reserved\">var<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">audio_context<\/span><span class=\"hl-code\">;\r\n  <\/span><span class=\"hl-reserved\">var<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">volume<\/span><span class=\"hl-code\">;\r\n \r\n  <\/span><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> one-off initialization<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-reserved\">function<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">init<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">g<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n    <\/span><span class=\"hl-reserved\">try<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n      <\/span><span class=\"hl-identifier\">audio_context<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-reserved\">new<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">g<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">AudioContext<\/span><span class=\"hl-code\"> || <\/span><span class=\"hl-identifier\">g<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">webkitAudioContext<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n      <\/span><span class=\"hl-identifier\">fire<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">Audio context OK<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n      <\/span><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> shim<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n      <\/span><span class=\"hl-builtin\">navigator<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">getUserMedia<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-builtin\">navigator<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">getUserMedia<\/span><span class=\"hl-code\"> || <\/span><span class=\"hl-builtin\">navigator<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">webkitGetUserMedia<\/span><span class=\"hl-code\">;\r\n      <\/span><span class=\"hl-identifier\">fire<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">navigator.getUserMedia <\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-code\"> + <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-builtin\">navigator<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">getUserMedia<\/span><span class=\"hl-code\"> ? <\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">OK<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-code\"> : <\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">fail<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n      <\/span><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> use<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n      <\/span><span class=\"hl-builtin\">navigator<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">getUserMedia<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-code\">\r\n        <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-identifier\">audio<\/span><span class=\"hl-code\">:<\/span><span class=\"hl-reserved\">true<\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-code\">,\r\n        <\/span><span class=\"hl-identifier\">iCanHazUserMedia<\/span><span class=\"hl-code\">, \r\n        <\/span><span class=\"hl-reserved\">function<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">e<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-identifier\">fire<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">No live audio input <\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-code\"> + <\/span><span class=\"hl-identifier\">e<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-code\">\r\n      <\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n    <\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-reserved\">catch<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">e<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n      <\/span><span class=\"hl-identifier\">alert<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">No web audio support in this browser<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n    <\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-builtin\">window<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>When the user loads the page, here's what they see:<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/www.phpied.com\/wp-content\/uploads\/2012\/10\/Live-input.jpg\" alt=\"\" title=\"Live input\" width=\"650\" height=\"271\" class=\"aligncenter size-full wp-image-1670\" srcset=\"https:\/\/www.phpied.com\/wp-content\/uploads\/2012\/10\/Live-input.jpg 650w, https:\/\/www.phpied.com\/wp-content\/uploads\/2012\/10\/Live-input-300x125.jpg 300w\" sizes=\"(max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>In my case I select the guitar amp and click \"Allow\" button.<\/p>\n<p>This little window informs me the page is using the audio input:<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/www.phpied.com\/wp-content\/uploads\/2012\/10\/SystemUIServer.jpg\" alt=\"\" title=\"SystemUIServer\" width=\"332\" height=\"131\" class=\"aligncenter size-full wp-image-1671\" srcset=\"https:\/\/www.phpied.com\/wp-content\/uploads\/2012\/10\/SystemUIServer.jpg 332w, https:\/\/www.phpied.com\/wp-content\/uploads\/2012\/10\/SystemUIServer-300x118.jpg 300w\" sizes=\"(max-width: 332px) 100vw, 332px\" \/><\/p>\n<h3>Playing back<\/h3>\n<p>Now that the user has allowed audio access, let's play back the audio we receive, but pass it through a volume control.<\/p>\n<p>All this work happens in the <code>iCanhazUserMedia()<\/code>, the success callback to <code>getUserMedia<\/code>.<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-code\">  <\/span><span class=\"hl-reserved\">function<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">iCanHazUserMedia<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">stream<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n    \r\n    <\/span><span class=\"hl-identifier\">fire<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">I haz live stream<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n    \r\n    <\/span><span class=\"hl-reserved\">var<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">input<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-identifier\">audio_context<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">createMediaStreamSource<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">stream<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n    <\/span><span class=\"hl-identifier\">volume<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-identifier\">audio_context<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">createGainNode<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n    <\/span><span class=\"hl-identifier\">volume<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">gain<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">value<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-number\">0.8<\/span><span class=\"hl-code\">;\r\n    <\/span><span class=\"hl-identifier\">input<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">connect<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">volume<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n    <\/span><span class=\"hl-identifier\">volume<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">connect<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">audio_context<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">destination<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n    \r\n    <\/span><span class=\"hl-identifier\">fire<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">input connected to destination<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n  <\/span><span class=\"hl-brackets\">}<\/span><\/pre>\n<\/div>\n<p>What we have here (ignoring <code>fire()<\/code>):<\/p>\n<ol>\n<li>setup an input stream from the user stream, this is the first node in the audio chain<\/li>\n<li>setup a volume (Gain) node with initial volume 0.8 out of 1<\/li>\n<li>connect input to volume to output\/speakers<\/li>\n<\/ol>\n<p>And this is it!<\/p>\n<p>Additionally an <code>input type=range max=1 step=0.1<\/code> can change the volume via <code>volume.gain.value = value;<\/code><\/p>\n<p><a href=\"https:\/\/www.phpied.com\/files\/webaudio\/getusermedia.html\">Go play!<\/a> Isn't it amazing that you can now grab microphone or any other audio input and play around with it? All in JavaScript, all in the browser without any plugins.<\/p>\n<h3>Moar!<\/h3>\n<p>This was a very basic exploratory\/primer example. For more:<\/p>\n<ul>\n<li><a href=\"http:\/\/dashersw.github.com\/pedalboard.js\/demo\/\">Pedalboard.js<\/a> with more effects besides volume<\/li>\n<li>One of Chris Wilson's demos has <a href=\"http:\/\/webaudiodemos.appspot.com\/input\/index.html\">even more effects<\/a><\/li>\n<li>and how 'bout <a href=\"http:\/\/phenomnomnominal.github.com\/\">a guitar tuna<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Live input, aka getUserMedia: it exists in Chrome Canary for audio too. Great times to be a web developer, right? Let&#8217;s check it out. Here&#8217;s the demo, but first a prerequisite: go chrome:\/\/flags, search for &#8220;Web Audio Input&#8221; and enable it. Restart Chrome Canary. With a guitar I wanted to have a little trickier setup [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[5,7,365],"tags":[],"_links":{"self":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts\/1669"}],"collection":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/comments?post=1669"}],"version-history":[{"count":0,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts\/1669\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/media?parent=1669"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/categories?post=1669"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/tags?post=1669"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}