質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.38%
Web Audio

Web Audioは、音声を処理・合成するためのWebアプリケーション向けJavaScript APIです。HTML5から導入されました。オーディオソースの選択、エフェクト・ビジュアライゼーションの追加、パンニングなど特殊効果の適用など多くの機能を持ちます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

WebRTC

WebRTC(Web Real-Time Communication)とは、プラグイン無しでウェブブラウザ間の音声通話・ビデオチャットなどリアルタイムコミュニケーションができるオープンフレームワークです。W3CがAPIレベルで、IETFがプロトコルレベルでそれぞれ標準化が進められています。

Q&A

解決済

1回答

10819閲覧

PC内で流れる音源の音声データをJSで取得する方法を教えてください!

afroscript

総合スコア148

Web Audio

Web Audioは、音声を処理・合成するためのWebアプリケーション向けJavaScript APIです。HTML5から導入されました。オーディオソースの選択、エフェクト・ビジュアライゼーションの追加、パンニングなど特殊効果の適用など多くの機能を持ちます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

WebRTC

WebRTC(Web Real-Time Communication)とは、プラグイン無しでウェブブラウザ間の音声通話・ビデオチャットなどリアルタイムコミュニケーションができるオープンフレームワークです。W3CがAPIレベルで、IETFがプロトコルレベルでそれぞれ標準化が進められています。

0グッド

2クリップ

投稿2017/01/22 15:37

WebRTCやWebAudioなどを使って、PC内で流れる音声を取得しようとしているのですが、方法が分かりません。。。

具体的に言うと、
Youtubeで流れる音を波形データとして取得し、
その音声の波長データを使って3Dの物体生成する、
というのをやっております。

ちなみに近い完成イメージでいうと、今試しているのは、
WebRTCのnavigator.getUserMediaよりPC内臓のマイクの音を拾ってきて、
その波形データを使って3D表現するというのをやっております。
(Youtubeで流したものをわざわざマイクから拾うという、すごく遠回りな方法です。。。)

javascript

1/* 2 * PCのマイクから音をひろってくる 3 */ 4 5var getUserMedia = navigator.getUserMedia ? 'getUserMedia' : 6 navigator.webkitGetUserMedia ? 'webkitGetUserMedia' : 7 navigator.mozGetUserMedia ? 'mozGetUserMedia' : 8 navigator.msGetUserMedia ? 'msGetUserMedia' : 9 undefined; 10var astream, micsrc; 11var conditions={audio:true, video:false}; 12const Mic = () => { 13 navigator[getUserMedia]( 14 conditions, 15 (stream) => { 16 astream=stream; 17 micsrc=audioctx.createMediaStreamSource(stream); 18 micsrc.connect(audioctx.destination); 19 micsrc.connect(analyser); 20 }, 21 (e) => { console.error(e); } 22 ); 23} 24 25//Mic集音開始 26Mic(); 27 28 29/* 30 * 音声ファイルデータを使用して3Dオブジェクトを生成 31 */ 32 33var audioctx = new AudioContext(); 34 35var timerId; 36var analyser = audioctx.createAnalyser(); 37analyser.fftSize = 128; 38analyser.minDecibels = -100; 39analyser.maxDecibels = -30; 40 41var obj; 42const DrawGraph = () => { 43 44 var data = new Uint8Array(512); 45 46 scene.remove(obj); 47 48 analyser.getByteFrequencyData(data); //Spectrum Dataで取得 49 50 //Spectrum Dataの値を使って3Dオブジェクト生成 51 var geo = new THREE.TorusKnotGeometry( //小数をかけてるのは値を小さくして、3Dオブジェクトのサイズを小さくするため 52 Math.round(data[0] * 2.0), //全体的な大きさ 53 Math.round(data[1] * 0.3), //チューブの太さ 54 Math.round(40), //クネクネの進む方向に対してなん分割するか 55 Math.round(8), //チューブ方向に何分割するか 56 Math.round(data[2] * 0.3), //なんかクネクネ具合が変わる数値その1 57 Math.round(2) //なんかクネクネ具合が変わる数値その2 58 ); 59 60 var mat = new THREE.MeshBasicMaterial({color: 0xffffff, wireframe:true }); 61 obj = new THREE.Mesh( geo, mat); 62 scene.add(obj); 63 64 requestAnimationFrame(DrawGraph); 65} 66timerId=requestAnimationFrame(DrawGraph);

これと同様なことを、PC内で流れる音のみの音声データを拾って、実現したいなと思っているのですが、その方法/調べ方が分からず。。。

丸投げちっくな質問の仕方になってしまい大変恐縮なのですが、ぜひ検索ワードのヒントでもいいので、教えていただけると幸いです...!!

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

#私が考えた最強の方法

##MediaElement.captureStream()
MediaElementにはcaptureStream()という機能があり、MediaElementから直接MediaStreamを取得することができるメソッドです。
<canvas>にはすでにcaptureStream()がサポートされているのですが、まだ<video><audio>はサポートされていません。
ただ、現在はデフォルトで無効になっているという状態で、chrome://flags の"試験運用版のウェブ プラットフォームの機能"(chrome://flags/#enable-experimental-web-platform-features)を有効に設定することで<video><audio>でも使用できるようになります。

##拡張機能
Youtubeの埋め込みプレイヤーはiframeで埋め込まれるため、コンテンツ(親フレーム)のJSからプレイヤーの<video>にアクセスすることができないため、拡張機能を使用してアクセスします。
拡張機能および、captureStream()の機能を使用することで、Youtube埋め込みプレイヤーから直接Video/Audioストリームを取得することができるようになります。

##公開するには
ブラウザーでcaputureStream()がデフォルトで有効になるバージョンになるまで待てればそれで構いませんし、またユーザーに実験中の機能を有効にしてもらうというのが嫌でなければそのまま公開してもいいでしょう。
ユーザーに有効にしてもらうのが嫌というのでしたら、Origin Trialsという仕組みがあります。これは、Originと有効に設定するフラグを登録することで、ユーザーの設定作業無しに、そのOriginにアクセスすると自動的に登録した実験中の機能が有効になるというものです。
詳しくは、Web 標準化のフィードバックサイクルを円滑にする Origin Trials についてを見てください。

##サンプルコード
####コンテンツページ
index.html

HTML

1<!DOCTYPE html> 2<html> 3<head> 4 <script src="three.min.js"></script> 5 <script src="OrbitControls.js"></script> 6 <style> 7 html, body { 8 width: 100%; 9 height: 100%; 10 margin: 0; 11 } 12 13 iframe { 14 width: 360px; 15 height: 200px; 16 left: 0; 17 top: 0; 18 position: absolute; 19 z-index: 1; 20 } 21 </style> 22</head> 23<body> 24 <div id="player"></div> 25 <script src="app.js"></script> 26</body> 27</html>

app.js

js

1const extId = 'bbchnfmcdcokmipmngjjniflkoakncbl'; 2chrome.runtime.sendMessage(extId, 'content tab handle'); 3 4const tag = document.createElement('script'); 5tag.src = "https://www.youtube.com/iframe_api"; 6const firstScriptTag = document.getElementsByTagName('script')[0]; 7firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); 8let player; 9let inited = false; 10const vid = 'bHQqvYy5KYo'; 11 12const renderer = new THREE.WebGLRenderer({ 13 antialias: true 14}); 15renderer.setSize(window.innerWidth, window.innerHeight); 16document.body.appendChild(renderer.domElement); 17const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000); 18camera.position.set(0, 0, 300); 19const controls = new THREE.OrbitControls(camera); 20const scene = new THREE.Scene(); 21const obj = new THREE.Mesh(); 22obj.material = new THREE.MeshBasicMaterial({ 23 color: 0xffffff, 24 wireframe: true 25}); 26obj.geometry = new THREE.TorusKnotGeometry(0, 0, 0, 0, 0, 0); 27scene.add(camera); 28scene.add(obj); 29 30window.addEventListener('resize', () => { 31 renderer.setSize(window.innerWidth, window.innerHeight); 32 camera.aspect = window.innerWidth / window.innerHeight; 33 camera.updateProjectionMatrix(); 34}) 35 36function onYouTubeIframeAPIReady() { 37 player = new YT.Player('player', { 38 height: 200, 39 width: 200, 40 videoId: vid, 41 events: { 42 onReady: onPlayerReady, 43 onStateChange: onPlayerStateChange 44 } 45 }); 46} 47 48function onPlayerReady(event) { 49 event.target.playVideo(); 50 DrawGraph(); 51} 52 53function onPlayerStateChange(event) { 54 if (event.data === YT.PlayerState.PLAYING && !inited) { 55 chrome.runtime.sendMessage(extId, 'ready'); 56 inited = true; 57 } 58} 59 60function stopVideo() { 61 player.stopVideo(); 62} 63 64function DrawGraph() { 65 requestAnimationFrame(DrawGraph); 66 chrome.runtime.sendMessage(extId, 'get audio data', function(spectrums) { 67 obj.geometry.dispose(); 68 obj.geometry = new THREE.TorusKnotGeometry( //小数をかけてるのは値を小さくして、3Dオブジェクトのサイズを小さくするため 69 Math.round(spectrums[0] * 2.0), //全体的な大きさ 70 Math.round(spectrums[1] * 0.3), //チューブの太さ 71 Math.round(40), //クネクネの進む方向に対してなん分割するか 72 Math.round(8), //チューブ方向に何分割するか 73 Math.round(spectrums[2] * 0.3), //なんかクネクネ具合が変わる数値その1 74 Math.round(2) //なんかクネクネ具合が変わる数値その2 75 ); 76 }); 77 controls.update(); 78 renderer.render(scene, camera); 79}

####拡張機能
manifest.json

json

1{ 2 "manifest_version": 2, 3 "version": "0.0.1", 4 "name": "埋め込みプレイヤーオーディオデータ取得テスト", 5 "content_scripts": [{ 6 "matches": [ 7 "https://www.youtube.com/embed/*" 8 ], 9 "js": ["iframe_content.js"], 10 "run_at": "document_start", 11 "all_frames": true 12 }], 13 "background": { 14 "scripts": ["background.js"] 15 }, 16 "externally_connectable": { 17 "matches": [ 18 "https://turbographics2000.github.io/get_youtube_audiodata/" 19 ] 20 } 21}

background.js

js

1let contentTab = null; 2chrome.runtime.onMessageExternal.addListener(handleMessage); 3chrome.runtime.onMessage.addListener(handleMessage) 4 5function handleMessage(msg, sender, res) { 6 console.log(msg); 7 switch (msg) { 8 case 'content tab handle': 9 contentTab = sender.tab.id; 10 console.log('contentTab', contentTab); 11 break; 12 case 'ready': 13 chrome.tabs.sendMessage(contentTab, 'ready'); 14 break; 15 case 'get audio data': 16 if (!contentTab) return; 17 chrome.tabs.sendMessage(contentTab, msg, (data) => { 18 res(data); 19 }); 20 break; 21 } 22 return true; 23}

iframe_content.js

js

1const audioctx = new AudioContext(); 2const analyser = audioctx.createAnalyser(); 3 4chrome.runtime.onMessage.addListener((msg, sender, res) => { 5 switch (msg) { 6 case 'ready': 7 let video = document.getElementsByTagName('video')[0]; 8 let stream = video.captureStream(); 9 let micsrc = audioctx.createMediaStreamSource(stream); 10 analyser.fftSize = 128; 11 analyser.minDecibels = -100; 12 analyser.maxDecibels = -30; 13 micsrc.connect(analyser); 14 break; 15 case 'get audio data': 16 let spectrums = new Uint8Array(analyser.frequencyBinCount); 17 analyser.getByteFrequencyData(spectrums); 18 res(spectrums); 19 break; 20 } 21});

##デモページ
デモページを用意しました。
"試験運用版のウェブ プラットフォームの機能"(chrome://flags/#enable-experimental-web-platform-features)を有効にし(ブラウザーの再起動)、サンプル拡張機能を先にインストールしてから、デモページにアクセスしてください。

##謝辞
サンプルコードおよびデモページで、質問のコードを使用させていただきました。

##最後に
確実にピンポイントなストリームを取得することができるというのが一番の利点ですが、見ての通り、単純にgetUserMedia()を使用するより、こちらのほうが実装においてははるかに回りくどいものとなっています。
あと、質問のコードではnavigator.getUserMedia()を使用しているようですが、これは古い仕様のものです。
navigator.mediaDevices.getUserMedia()を使用するようにしましょう(Promise)(prefix解決も不要です)。

投稿2017/01/23 08:57

編集2017/01/23 09:09
turbgraphics200

総合スコア4269

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

afroscript

2017/01/23 11:45

おぉっ!!!!すごい!!!!! できるのですね!!!! 周りの方々から、かなり厳しいとの意見もらってたのであきらめかけてました!! 本当にありがとうございます!試します!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.38%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問