web-dev-qa-db-ja.com

javascript / html5を使用してオンザフライでサウンドを生成する

Javascript/html5を使用して一定のサウンドストリームを生成することは可能ですか?たとえば、永久正弦波を生成するには、出力バッファーが空になろうとするたびに呼び出されるコールバック関数を使用します。

function getSampleAt(timestep)
{
    return Math.sin(timestep);
}

(アイデアは、これを使用してインタラクティブなシンセを作成することです。キーが押される時間を事前に知りませんので、固定長のバッファを使用できません)

64
Johannes Hoff

HTML5オーディオ要素を使用する

JavaScriptとaudio要素を使用したクロスブラウザの生成された持続的オーディオは、現時点では不可能です。StevenWittens JavaScriptシンセの作成に関するブログ投稿のメモ:

「...シームレスな再生のために合成オーディオのチャンクをキューに入れる方法はありません」。

Web Audio APIを使用する

Web Audio API は、JavaScriptオーディオ合成を容易にするために設計されました。 Mozilla Developer Networkには Webベースのトーンジェネレーターがあります Firefox 4以降で動作します[ demo 1 ]。このコードに次の2行を追加すると、キーを押すと生成された持続オーディオを備えた動作するシンセが作成されます[ デモ2 -Firefox 4でのみ動作し、最初に[結果]エリアをクリックしてから任意のキーを押します):

window.onkeydown = start;  
window.onkeyup = stop;

BBCの Web Audio APIのページ も確認する価値があります。残念ながら、Web Audio APIのサポートはまだ他のブラウザーに拡張されていません。

考えられる回避策

現在、クロスブラウザシンセを作成するには、事前に録音されたオーディオを使用する必要があります。

  1. 事前に録音された長いogg/mp3サンプルトーンを使用し、それらを個別のaudio要素に埋め込み、キーを押すと開始および停止します。
  2. オーディオ要素を含むswfファイルを埋め込み、JavaScriptを介して再生を制御します。 (これは、 Google Les Paul Doodle が採用している方法のようです。)
25
Nick

現在、ほとんどのブラウザで Web Audio API を使用できます( IEおよびOpera Mini を除く) 。

このコードを試してください:

// one context per document
var context = new (window.AudioContext || window.webkitAudioContext)();
var osc = context.createOscillator(); // instantiate an oscillator
osc.type = 'sine'; // this is the default - also square, sawtooth, triangle
osc.frequency.value = 440; // Hz
osc.connect(context.destination); // connect it to the destination
osc.start(); // start the oscillator
osc.stop(context.currentTime + 2); // stop 2 seconds after the current time

ボリュームを小さくしたい場合は、次のようなことができます。

var context = new webkitAudioContext();
var osc = context.createOscillator();
var vol = context.createGain();

vol.gain.value = 0.1; // from 0 to 1, 1 full volume, 0 is muted
osc.connect(vol); // connect osc to vol
vol.connect(context.destination); // connect vol to context destination
osc.start(context.currentTime + 3); // start it three seconds from now

@brainjamのリンクから見つけた Web Audio API Working Draft を読みながら、クロムの実験からこれのほとんどを得ました。

それがお役に立てば幸いです。最後に、chromeインスペクター(ctrl-shift-i)でさまざまなオブジェクトを検査することは非常に役立ちます。

53
snapfractalpop

Web Audio APIがChromeに登場します。 http://googlechrome.github.io/web-audio-samples/samples/audio/index.html を参照してください

そこにある「はじめに」の指示に従って、非常に印象的なデモをご覧ください。

Update(2017):今では、これははるかに成熟したインターフェースです。 APIは https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API で文書化されています

11
brainjam

もちろん!このデモでは、トーンシンセサイザーを使用できます。

enter image description here

audioCtx = new(window.AudioContext || window.webkitAudioContext)();

show();

function show() {
  frequency = document.getElementById("fIn").value;
  document.getElementById("fOut").innerHTML = frequency + ' Hz';

  switch (document.getElementById("tIn").value * 1) {
    case 0: type = 'sine'; break;
    case 1: type = 'square'; break;
    case 2: type = 'sawtooth'; break;
    case 3: type = 'triangle'; break;
  }
  document.getElementById("tOut").innerHTML = type;

  volume = document.getElementById("vIn").value / 100;
  document.getElementById("vOut").innerHTML = volume;

  duration = document.getElementById("dIn").value;
  document.getElementById("dOut").innerHTML = duration + ' ms';
}

function beep() {
  var oscillator = audioCtx.createOscillator();
  var gainNode = audioCtx.createGain();

  oscillator.connect(gainNode);
  gainNode.connect(audioCtx.destination);

  gainNode.gain.value = volume;
  oscillator.frequency.value = frequency;
  oscillator.type = type;

  oscillator.start();

  setTimeout(
    function() {
      oscillator.stop();
    },
    duration
  );
};
frequency
<input type="range" id="fIn" min="40" max="6000" oninput="show()" />
<span id="fOut"></span><br>
type
<input type="range" id="tIn" min="0" max="3" oninput="show()" />
<span id="tOut"></span><br>
volume
<input type="range" id="vIn" min="0" max="100" oninput="show()" />
<span id="vOut"></span><br>
duration
<input type="range" id="dIn" min="1" max="5000" oninput="show()" />
<span id="dOut"></span>
<br>
<button onclick='beep();'>Play</button>

楽しむ!

ここでHoushalterから解決策を得ました: Javascriptのビープ音を鳴らすにはどうすればよいですか?

ここでコードを複製して微調整できます: JS Binのトーンシンセサイザーデモ

互換性のあるブラウザ:

  • Chromeモバイルとデスクトップ
  • Firefoxモバイル&デスクトップOperaモバイル、ミニ&デスクトップ
  • Androidブラウザー
  • Microsoft Edgeブラウザー
  • IPhoneまたはiPadのSafari

互換性がありません

  • Internet Explorerバージョン11(ただし、Edgeブラウザーでは機能します)
8
CaptureWiz

その場でwav-eファイルを生成して再生できます( src

// Legend
// DUR - duration in seconds   SPS - sample per second (default 44100)
// NCH - number of channels    BPS - bytes per sample

// t - is number from range [0, DUR), return number in range [0, 1]
function getSampleAt(t,DUR,SPS)
{
    return Math.sin(6000*t); 
}

function genWAVUrl(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
  let size = DUR*NCH*SPS*BPS; 
  let put = (n,l=4) => [(n<<24),(n<<16),(n<<8),n].filter((x,i)=>i<l).map(x=> String.fromCharCode(x>>>24)).join('');
  let p = (...a) => a.map( b=> put(...[b].flat()) ).join(''); 
  let data = `RIFF${put(44+size)}WAVEfmt ${p(16,[1,2],[NCH,2],SPS,NCH*BPS*SPS,[NCH*BPS,2],[BPS*8,2])}data${put(size)}`
  
  for (let i = 0; i < DUR*SPS; i++) {
    let f= Math.min(Math.max(fun(i/SPS,DUR,SPS),0),1);
    data += put(Math.floor( f * (2**(BPS*8)-1)), BPS);
  }
  
  return "data:Audio/WAV;base64," + btoa(data);
}


var WAV = new Audio( genWAVUrl(getSampleAt,5) ); // 5s
WAV.setAttribute("controls", "controls");
document.body.appendChild(WAV);
//WAV.play()

ここに視覚化があります

function getSampleAt(t,DUR,SPS)
{
    return 0.5+Math.sin(15*t)/(1+t*t); 
}


// ----------------------------------------------

function genWAVUrl(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
  let size = DUR*NCH*SPS*BPS; 
  let put = (n,l=4) => [(n<<24),(n<<16),(n<<8),n].filter((x,i)=>i<l).map(x=> String.fromCharCode(x>>>24)).join('');
  let p = (...a) => a.map( b=> put(...[b].flat()) ).join(''); 
  let data = `RIFF${put(44+size)}WAVEfmt ${p(16,[1,2],[NCH,2],SPS,NCH*BPS*SPS,[NCH*BPS,2],[BPS*8,2])}data${put(size)}`
  
  for (let i = 0; i < DUR*SPS; i++) {
    let f= Math.min(Math.max(fun(i/SPS,DUR,SPS),0),1);
    data += put(Math.floor( f * (2**(BPS*8)-1)), BPS);
  }
  
  return "data:Audio/WAV;base64," + btoa(data);
}

function draw(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
  time.innerHTML=DUR+'s';
  time.setAttribute('x',DUR-0.3);
  svgCh.setAttribute('viewBox',`0 0 ${DUR} 1`);
  let p='', n=100; // n how many points to ommit
  for (let i = 0; i < DUR*SPS/n; i++) p+= ` ${DUR*(n*i/SPS)/DUR}, ${1-fun(n*i/SPS, DUR,SPS)}`;
  chart.setAttribute('points', p);
}

function frame() {
  let t=WAV.currentTime;
  point.setAttribute('cx',t)
  point.setAttribute('cy',1-getSampleAt(t))
  window.requestAnimationFrame(frame);
}

function changeStart(e) {
  var r = e.target.getBoundingClientRect();
  var x = e.clientX - r.left;
  WAV.currentTime = dur*x/r.width;
  WAV.play()
}

var dur=5; // seconds 
var WAV = new Audio(genWAVUrl(getSampleAt,dur));
draw(getSampleAt,dur);
frame();
.chart { border: 1px dashed #ccc; }
.axis { font-size: 0.2px}
audio { outline: none; }
Click at blue line (make volume to max):
<svg class="chart" id="svgCh" onclick="changeStart(event)">    
  <circle cx="0" cy="-1" r="0.05" style="fill: rgba(255,0,0,1)" id="point"></circle>
  <polyline id="chart" fill="none" stroke="#0074d9" stroke-width="0.01" points=""/>
  <text x="0.03" y="0.9" class="axis">0</text>
  <text x="0.03" y="0.2" class="axis">1</text>
  <text x="4.8" y="0.9" class="axis" id="time"></text>
</svg><br>
2

JavaScriptソリューションを求めているため、これは質問に対する本当の答えではありませんが、ActionScriptを使用できます。すべての主要なブラウザーで実行する必要があります。

JavaScript内からActionScript関数を呼び出すことができます。

そのようにして、ActionScriptサウンド生成関数をラップし、それらのJavaScript実装を作成できます。 Adobe Flexを使用して小さなswfを作成し、それをJavaScriptコードのバックエンドとして使用するだけです。

1
Mara Maya

これは私が永遠に探していたものであり、最終的には自分がやりたいように自分でやることができました。たぶん、あなたもそれを好きになるでしょう。周波数とプッシュのオン/オフを備えたシンプルなスライダー:

buttonClickResult = function () {
        var button = document.getElementById('btn1');

        button.onclick = function buttonClicked()  {

                if(button.className=="off")  {
                        button.className="on";
                        oscOn ();
                }

                else if(button.className=="on")  {
                        button.className="off";
                        oscillator.disconnect();
                }
        }
};

buttonClickResult();

var oscOn = function(){

        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        var context = new AudioContext();
        var gainNode = context.createGain ? context.createGain() : context.createGainNode();

        //context = new window.AudioContext();
        oscillator = context.createOscillator(),
                        oscillator.type ='sine';

        oscillator.frequency.value = document.getElementById("fIn").value;
        //gainNode = createGainNode();
        oscillator.connect(gainNode);
        gainNode.connect(context.destination);
        gainNode.gain.value = 1;
        oscillator.start(0);
};
<p class="texts">Frekvence [Hz]</p>
<input type="range" id="fIn" min="20" max="20000" step="100" value="1234" oninput="show()" />
<span id="fOut"></span><br>
<input class="off" type="button" id="btn1" value="Start / Stop" />
0
Xenon