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

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

新規登録して質問してみよう
ただいま回答率
85.48%
WebGL

WebGL(ウェブジーエル)は、ウェブブラウザで 3次元コンピュータグラフィックスを表示させるための標準仕様です。

Q&A

解決済

1回答

1281閲覧

WebGL×webAudioAPIで音を鳴らしたい

YukiShimada

総合スコア17

WebGL

WebGL(ウェブジーエル)は、ウェブブラウザで 3次元コンピュータグラフィックスを表示させるための標準仕様です。

1グッド

0クリップ

投稿2018/04/10 08:26

表題の件について質問です.

以前にqiitaで,このような記事を書かせていただき,https://qiita.com/ukeyshima/items/b1cb4291de9f1a065de5
webGLで音を作成し,webAudioAPIで音を流すプログラムを作成しました.
これはshadertoyなどでも使われている手法です.

しかし,僕のプログラムでは,必ず節目節目にブツッという音が入ってしまい,ずっと放置していた状態です.
正直音について詳しくなく,音の記述の方がおかしいのか,プログラムがおかしいのかがわかっていません.おそらく,setIntervalなどの問題だと思っていいて,https://www.html5rocks.com/ja/tutorials/audio/scheduling/
の記事などを参考にしようとは思っているのですが,イマイチピンとこずコードに起こせない状態です.

プログラムの構造などはqiitaの記事をみていただけるとわかると思います.もしどなたか同じようなコードを書いた方がいらっしゃれば解決策をご教授いただけると幸いです.よろしくお願いいたします.

javascript

1 2window.addEventListener("load", function () { 3 const c = document.createElement("canvas"); 4 let cw = window.innerWidth; 5 let ch = window.innerHeight; 6 c.width = cw; c.height = ch; 7 const gl = c.getContext("webgl2") || c.getContext("webgl") || c.getContext("experimental-webgl"); 8 const prg = create_program(create_shader("vs"), create_shader("fs")); 9 const uniLocation=new Array(); 10 uniLocation[0]=gl.getUniformLocation(prg,"iTime"); 11 let audioContext = new (window.AudioContext || window.webkitAudioContext)(); 12 let source = audioContext.createBufferSource(); 13 let sampleNum = 44100; 14 let time=0; 15 16 const play=()=>{ 17 gl.uniform1f(uniLocation[0],time); 18 const vTransformFeedback = gl.createBuffer(); 19 const transformFeedback = gl.createTransformFeedback(); 20 gl.bindBuffer(gl.ARRAY_BUFFER, vTransformFeedback); 21 gl.bufferData(gl.ARRAY_BUFFER, sampleNum * Float32Array.BYTES_PER_ELEMENT, gl.DYNAMIC_COPY); 22 gl.bindBuffer(gl.ARRAY_BUFFER, null); 23 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); 24 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, vTransformFeedback); 25 gl.beginTransformFeedback(gl.POINTS); 26 gl.drawArrays(gl.POINTS, 0, sampleNum); 27 gl.endTransformFeedback(); 28 let arrBuffer = new Float32Array(sampleNum); 29 gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, arrBuffer); 30 console.log(arrBuffer); 31 let audioBuffer = audioContext.createBuffer(2, sampleNum, sampleNum); 32 for (let i = 0; i < 2; i++) { 33 let bufferring = audioBuffer.getChannelData(i); 34 bufferring.set(arrBuffer); 35 } 36 let source = audioContext.createBufferSource(); 37 source.buffer = audioBuffer; 38 source.connect(audioContext.destination); 39 //source.loop = true; 40 source.start(); 41 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null); 42 time++; 43 44 } 45 setInterval(play,1000); 46 47 48 function create_shader(id) { 49 let shader; 50 const scriptElement = document.getElementById(id); 51 if (!scriptElement) { return; } 52 switch (scriptElement.type) { 53 case "vertexShader.glsl": 54 shader = gl.createShader(gl.VERTEX_SHADER); 55 break; 56 case "fragmentShader.glsl": 57 shader = gl.createShader(gl.FRAGMENT_SHADER); 58 break; 59 default: 60 return; 61 } 62 gl.shaderSource(shader, scriptElement.text); 63 gl.compileShader(shader); 64 if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 65 return shader; 66 } else { 67 alert(gl.getShaderInfoLog(shader)); 68 console.log(gl.getShaderInfoLog(shader)); 69 } 70 } 71 function create_program(vs, fs) { 72 const program = gl.createProgram(); 73 gl.attachShader(program, vs); 74 gl.attachShader(program, fs); 75 gl.transformFeedbackVaryings(program, ["sound"], gl.SEPARATE_ATTRIBS); 76 gl.linkProgram(program); 77 if (gl.getProgramParameter(program, gl.LINK_STATUS)) { 78 gl.useProgram(program); 79 return program; 80 } else { 81 return null; 82 } 83 } 84});

html

1<!DOCTYPE html> 2<html> 3<head> 4 <script src="main.js"></script> 5 <script id="vs" type="vertexShader.glsl">#version 300 es 6 precision highp float; 7 uniform float iTime; 8 out float sound; 9 void main(void){ 10 float time=float(gl_VertexID)/44100.0+iTime; 11 sound=sin(6.2831*440.0*time); 12 }</script> 13 <script id="fs" type="fragmentShader.glsl">#version 300 es 14 void main(void){}</script> 15</head> 16<body> 17 <canvas id="canvas"></canvas> 18</body> 19 20</html>
defghi1977👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

1秒ごとにplayが呼び出されて、その中で長さ1秒の音を1つ作って鳴らす、という仕組みですよね。

長さ1秒の音を繋げて連続した音を出したいけどうまくいかない場合、原因として一般的に考えられるのは
1.1つ1つの音が鳴り始めるタイミングが、ちょうど1秒ごとになってない
2.長さ1秒の音の中で、正しい波形が生成できてない
3.n秒後に鳴り始めた音の波形の最後と、(n+1)秒後に鳴り始めた音の波形の最初がなめらかに繋がってない
4.鳴ってる最中の音の波形を書き換えてしまっている
5、自分で作った物以外のソフトウェアやハードウェアの不具合
6.1~5のうち、2つ以上の複合
ぐらいかと思います。

質問にあるコードの、JavaScriptのほうだけを少し書き換えてみました。
arrBuffer2に490Hzの矩形波を生成して、それを鳴らしています。(矩形波なので、「なめらかに繋がって」ではないんですけど周期が合っていれば問題ないはず。)
arrBufferは鳴らしません。
あと、source.startに引数を入れてます。質問にある
https://www.html5rocks.com/ja/tutorials/audio/scheduling/
の中で osc.start( time ); とかやってるみたいですけど、それと同じ目的のはずです。

JavaScript

1window.addEventListener("load", function () { 2 const c = document.createElement("canvas"); 3 let cw = window.innerWidth; 4 let ch = window.innerHeight; 5 c.width = cw; c.height = ch; 6 const gl = c.getContext("webgl2") || c.getContext("webgl") || c.getContext("experimental-webgl"); 7 const prg = create_program(create_shader("vs"), create_shader("fs")); 8 const uniLocation=new Array(); 9 uniLocation[0]=gl.getUniformLocation(prg,"iTime"); 10 let audioContext = new (window.AudioContext || window.webkitAudioContext)(); 11 let source = audioContext.createBufferSource(); 12 let sampleNum = 44100; 13 let time=0; 14 15 const play=()=>{ 16 gl.uniform1f(uniLocation[0],time); 17 const vTransformFeedback = gl.createBuffer(); 18 const transformFeedback = gl.createTransformFeedback(); 19 gl.bindBuffer(gl.ARRAY_BUFFER, vTransformFeedback); 20 gl.bufferData(gl.ARRAY_BUFFER, sampleNum * Float32Array.BYTES_PER_ELEMENT, gl.DYNAMIC_COPY); 21 gl.bindBuffer(gl.ARRAY_BUFFER, null); 22 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback); 23 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, vTransformFeedback); 24 gl.beginTransformFeedback(gl.POINTS); 25 gl.drawArrays(gl.POINTS, 0, sampleNum); 26 gl.endTransformFeedback(); 27 let arrBuffer = new Float32Array(sampleNum); 28 let arrBuffer2 = new Float32Array(sampleNum); 29 gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, arrBuffer); 30 //console.log(arrBuffer); 31 //console.log(time); 32 //console.log(audioContext.currentTime); 33 console.log(audioContext.currentTime - time); 34 let audioBuffer = audioContext.createBuffer(2, sampleNum, sampleNum); 35 for (let j = 0; j < sampleNum; j++) { 36 arrBuffer2[j] = ((j + 22) / 45) & 1; 37 } 38 //console.log(arrBuffer2); 39 for (let i = 0; i < 2; i++) { 40 let bufferring = audioBuffer.getChannelData(i); 41 bufferring.set(arrBuffer2); // arrBuffer or arrBuffer2 42 } 43 let source = audioContext.createBufferSource(); 44 source.buffer = audioBuffer; 45 source.connect(audioContext.destination); 46 //source.loop = true; 47 //source.start(); 48 source.start(time + 2, 0, 1); 49 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null); 50 time++; 51 52 } 53 setInterval(play,1000); 54 55 56 function create_shader(id) { 57 let shader; 58 const scriptElement = document.getElementById(id); 59 if (!scriptElement) { return; } 60 switch (scriptElement.type) { 61 case "vertexShader.glsl": 62 shader = gl.createShader(gl.VERTEX_SHADER); 63 break; 64 case "fragmentShader.glsl": 65 shader = gl.createShader(gl.FRAGMENT_SHADER); 66 break; 67 default: 68 return; 69 } 70 gl.shaderSource(shader, scriptElement.text); 71 gl.compileShader(shader); 72 if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 73 return shader; 74 } else { 75 alert(gl.getShaderInfoLog(shader)); 76 console.log(gl.getShaderInfoLog(shader)); 77 } 78 } 79 function create_program(vs, fs) { 80 const program = gl.createProgram(); 81 gl.attachShader(program, vs); 82 gl.attachShader(program, fs); 83 gl.transformFeedbackVaryings(program, ["sound"], gl.SEPARATE_ATTRIBS); 84 gl.linkProgram(program); 85 if (gl.getProgramParameter(program, gl.LINK_STATUS)) { 86 gl.useProgram(program); 87 return program; 88 } else { 89 return null; 90 } 91 } 92}); 93

もし、
・↑ のままではうまく繋がって鳴ってる
・↑ の // arrBuffer or arrBuffer2 というコメントの箇所を書き換えて arrBuffer を鳴らすようにしてみてもうまく繋がって鳴ってる
だったら、それで1つの課題は乗り越えたはず。

・↑ のままではうまく繋がって鳴ってる
・arrBuffer を鳴らすようにしてみたらうまく繋がってない
だとしたら、最初のほうに書いた原因の2.や3.あたりから調べてみたらいいんじゃないかと思います。

ただし、私はWebGLに対してはほぼ無力です。(他のことに関しても、わかってるとは言い難いですが)
自力で頑張るか、他の人を頼るかしたほうが良いです。

投稿2018/04/11 14:12

編集2018/04/12 14:06
okrt

総合スコア366

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

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

okrt

2018/04/12 14:05

ブラウザや環境によって異なるかもしれませんが、矩形波の生成は arrBuffer2[j] = (j / 45) & 1; よりも arrBuffer2[j] = ((j + 22) / 45) & 1; のほうが良いかもしれません。回答本文のコードを修正します。 私の環境では、Firefoxは同じですがGoogle Chromeで改善されました。
YukiShimada

2018/04/13 16:02

完璧です......!お恥ずかしい話正直全く理解が及んでいなかったので大変助かりました.まだ理解できているわけではないのですがこれからじっくり考えさせていただきます......!ご回答ありがとうございました!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問