Get to know MDN better
このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。
このチュートリアルでは、音の作成と変更、時間とスケジューリングについて述べます。サンプルの読み込み、エンベロープ、フィルター、ウェーブテーブル、周波数変調を紹介します。これらの用語に慣れていて、ウェブオーディオAPIによるアプリケーションの入門を探しているなら、あなたは正しい場所に来ました。
メモ: 下記デモのソースコードは GitHub の MDN webaudio-examples リポジトリーの step-sequencer サブディレクトリーにあります。ライブデモを見ることもできます。
これからとても単純なステップシーケンサーを見ていきます。
実際には、ライブラリーを使う方が簡単です。ウェブオーディオ API は、それを前提に構築されています。もっと複雑なものを作ろうとしているのであれば、 tone.js が始めるには最適な場所でしょう。しかし、私たちは学習の練習として、最初の原理からこのようなデモを作成する方法を示したいと思います。
インターフェイスはマスターコントローラーで構成されており、シーケンサーを再生/停止したり、BPM(1 分間の拍数)を調整して「音楽」を速くしたり遅くしたりすることができます。
4 つの異なる音(ボイス)を鳴らすことができます。各ボイスには 4 つのボタンがあり、音楽の 1 小節のそれぞれの拍に 1 つずつ対応しています。それらが有効になると、音符が音になります。楽器が演奏すると、この拍の集合を移動され、小節をループします。
各ボイスにはローカルコントロールもあり、ボイスを作成するために使用する各テクニック特有のエフェクトや引数を操作することができます。使用しているメソッドは以下の通りです。
| "Sweep" | 発振器、周期波 | OscillatorNode, PeriodicWave |
| "Pulse" | 発振器の乗算 | OscillatorNode |
| "Noise" | ランダムノイズバッファー、バイクワッドフィルター | AudioBuffer, AudioBufferSourceNode, BiquadFilterNode |
| "Dial up" | サンプル音を読み込んで再生 | BaseAudioContext/decodeAudioData, AudioBufferSourceNode |
メモ: 私たちがこのインストゥルメントを作成したのは、音を良くするためではなく、デモコードを提供するためです。このデモは、そのようなインストの非常に単純化されたバージョンを表します。音はダイアルアップモデムに基づいています。このような機器がどのような音で鳴るのかご存じない方は、こちらで試聴できます。
すぐに使用することになりますが、ウェブ音声 API アプリは音声コンテキストから始まります。
"sweep" 音、つまりダイヤルアップした時に聞こえる最初の音を呼び出すために、音を生成する発振器を作成します。
OscillatorNode で、基本的な波形(サイン波、矩形波、三角波、ノコギリ波)が決まります。しかし、既定値では標準の波形を使用する代わりに、 PeriodicWave インターフェイスと波形テーブルに設定した値を使って自分自身で作成します。このカスタム波を発振器で使用するには、PeriodicWave() コンストラクターを使用します。
最初に、周期波を作成します。そのためには、 PeriodicWave() コンストラクターに実数と虚数の値を渡す必要があります。
メモ: この例では、波動テーブルは非常に多くの値があるため、別個の JavaScript ファイル (wavetable.js) で保持されています。 Google Chrome Labs のウェブオーディオ API の例で得られる波形テーブルのリポジトリーから取りました。
これで OscillatorNode を作成し、その波を作成したものに設定することができます。
ここで関数に時刻の引数を渡していますが、これは後で掃除のスケジュールを立てるのに使用します。
これは素晴らしいことですが、振幅エンベロープがあればいいと思いませんか?単純なものを作成して、ウェブオーディオ API でエンベロープを作成するのに必要なメソッドを使用してみましょう。
エンベロープにアタックとリリースがあるとします。インターフェイスの範囲入力を使ってユーザーがこれらを制御できるようにします。
これで JavaScript で変数を作成し、入力値が更新されたときにその変数を変更するようにします。
これで playSweep() 関数を展開させることができました。 GainNode を追加して、それを音声グラフに接続して、音に振幅の変化を加える必要があります。ゲインノードは 1 つのプロパティを持っています。 gain で AudioParam 型です。
これは有用なことです。これで、ゲイン値に関する音声引数のメソッドのパワーを利用し始めることができます。ある時刻に値を設定することもできますし、 AudioParam.linearRampToValueAtTime のようなメソッドで時間を値を変更することもできます。
前述のように、アタックとリリースには linearRampToValueAtTime メソッドを使用します。このメソッドには 2 つの引数を取ります。設定したい引数の値(この場合はゲイン)と、いつ設定するかです。この場合、いつかは入力コントロールで制御します。例えば下記の例では、アタック範囲を定義する時刻の間、ゲインは直線的に 1 に増加します。同様に、リリースの場合、ゲインは、リリース入力が設定されている時刻にわたって、直線的な割合で 0 に集合します。
これで sweep ができました。次に移動して、素敵なパルス音を見てみましょう。基本的な発振器で、 2 つ目の発振器で変調することで実現できます。
最初の OscillatorNode はスイープ音と同じように設定しますが、独自の波形を設定するための波形テーブルを使用しません。
これで GainNode を作成します。 2 つ目の低周波発振器で発振させる gain 値です。
これで、最初のサイン波の増幅率を変えるために、 2 つ目の矩形波(またはパルス)発振器を作成します。
ここで重要なのは、グラフを正しく接続することと、両方の発振器を起動することです。
メモ: また、作成するどちらの発振器にも既定値の波形テーブルを使用する必要はありません。前と同じように波形テーブルと周期波メソッドを使用できます。最小のノードで、多くの可能性があります。
UI コントロールのために、発振器の両方の周波数を公開し、範囲入力で制御できるようにしましょう。一方は音程を変更し、もう一方はパルスが最初の波をどのように変調するかを変更します。
前回と同様に、ユーザーが引数の範囲を変更したときに、引数を変化させます。
playPulse() 関数の全体は以下のようになります。
ノイズを出すことが必要になりました。モデムにはすべてノイズがあります。音声データの場合、ノイズは単なる乱数なので、コードで作成するのは比較的簡単なことです。
しかし、ウェブオーディオ API が理解できる空のコンテナーを作成する必要があります。そこで AudioBuffer オブジェクトの出番です。ファイルを取得してバッファーにデコードすることもできますし(チュートリアルの後半で説明します)、空のバッファーを作成してデータを入れることもできます。
ノイズについては、後者で説明します。最初に、作成するバッファーサイズを計算する必要があります。これには BaseAudioContext.sampleRate プロパティが使用できます。
これで -1 から 1 の間の乱数で埋めることができます。
メモ: なぜ -1~1 なのでしょうか?ファイルやスピーカーに音を出力する場合、 0dB が上限のスケール(メディアや DAC の固定的な数値の限界)を表す数値が必要です。浮動小数点の音声では、 1 は信号の数学的処理をするために「上限」に割り当てられる便利な数字です。そのため、発振器、ノイズジェネレーター、そして他の音源は、通常 -1 から 1 の範囲の双極信号を出力します。ブラウザーはこの範囲外の値をクランプします。
これで音声バッファーができ、データが入りました。バッファーをソースとして使用できるノードをグラフに追加する必要があります。そのために AudioBufferSourceNode を作成し、作成したデータを渡します。
これを音声グラフにつないで再生します。
かなりヒスノイズというか、ちぐはぐな音であることにお気づきでしょう。ホワイトノイズを作成したのですから、そうあるべきです。値が-1から1まで広がっていますが、これはすべての周波数にピークがあることを意味しています。この関数を0.5から-0.5までの値のみに変更し、ピークを取り除き、不快感を縮小することも可能です。作成したノイズをフィルターに通してみましょう。
ピンクまたはブラウンノイズの範囲にあるものが必要です。高い周波数と単発の低い周波数をカットしたいのです。バンドパスバイクワッドフィルターを選びましょう。
メモ: ウェブオーディオ API には、 BiquadFilterNode と IIRFilterNode の 2 つの種類のフィルタノードがあります。ほとんどの場合、バイクワッドフィルタで十分です - ローパス、ハイパス、バンドパスなどの様々な種類があります。しかし、もっと特注のものを探しているのであれば、 IIR フィルターが良い選択肢かもしれません。詳しくは IIR フィルターの使用を参照してください。
この配線は、前に見たのと同じです。 BiquadFilterNode を作成し、必要なプロパティを設定し、グラフを通して接続します。例えば、バンドパス型で周波数を設定するには、中間周波数を調整します。しかし、ローパスでは、一番上の周波数を設定します。
UI 上では、前回と同じように、ノイズの継続時間と帯域したい周波数を公開し、ユーザーが範囲入力とイベントハンドラーを使って調整できるようにします。
playNoise() 関数の全体は次のようになります。
これまで使用してきたメソッドを使って、いくつかの発振器を一緒に鳴らすことで、電話のダイヤル音 (DTMF) をエミュレートすることは、十分に簡単です。代わりに、この節ではサンプルファイルを読み込んで、その内容を見ていきます。
使用する前にファイルが読み込まれ、バッファーにデコードされたことを確認したいので、 async 関数を作成してこれをできるようにしましょう。
そして、この関数を呼び出すときに await 演算子を使うことで、実行が完了したときに後続のコードを確実に実行することができます。
サンプルを設定するために、もう一つ async 関数を作成しましょう。この2つの非同期関数を素敵なプロミスパターンで結合することで、このファイルが読み込まれてバッファーされたときにさらなるアクションを実行することができます。
メモ: 上記の関数を簡単に変更し、ファイルの配列を引き継いでループさせ、複数のサンプルを読み込むことができます。このテクニックは、より複雑なインストゥルメントやゲームに便利でしょう。
We can now use setupSample() like so:
サンプルを再生する準備ができたら、プログラムに UI を設定すれば、使えるようになります。
他の音と同じように、 playSample() 関数を作成しましょう。今回は AudioBufferSourceNode を作成し、取得しデコードしたバッファーデータを入れて再生します:
メモ: stop() は AudioBufferSourceNode で呼び出すことができますが、これはサンプルの再生が完了したときに自動的に行われます。
AudioBufferSourceNode には playbackRate プロパティがあります。このプロパティを UI に公開し、サンプルを速くしたり遅くしたりできるようにしましょう。先ほどと同じような方法で行います。
次に playSample() 関数に playbackRate プロパティを更新する行を追加します。最終的にはこのようになります。
メモ: この音声ファイルは soundbible.com から引用しました。
デジタル音声アプリケーションでよくある問題は、ビートが一定に保たれ、時刻がずれないように音を再生することです。
for ループの中で再生するように音色をスケジュールすることもできますが、この場合の最大の問題は再生中に更新することで、そのための UI コントロールはすでに実装しています。また、楽器全体の BPM コントロールを検討するのも実にいいでしょう。音符がいつ演奏されるかを先に見て、それをキューに入れるのです。 currentTime プロパティで正確な時刻に始めることができ、変更も考えることができます。
メモ: この記事は、 Chris Wilson's A Tale Of Two Clocks (2013) の記事を大幅に縮小したもので、このメソッドについてもっと詳しく書かれています。ここですべてを繰り返す意味はありませんが、この記事を読んでこのメソッドを使用することを強くお勧めします。ここでのコードの多くは彼のメトロノームの例から引用しています。彼はこの記事の中で参照しています。
既定の BPM (beats per minute) を設定することから始めましょう。この BPM はユーザーも制御することができます。
そして、どれくらい先まで見ていくのか、どれくらい先のスケジュールを定義する変数を作成します。
音符を 1 拍分前に移動し、 4 番目(最後)の音符に到達したら最初の音符にループで戻る関数を作成してみましょう。
演奏する音符の参照キューと、前回作成した関数を使用して演奏する機能を作成したいと思います。
ここでは、現在の時刻を見て、次の音符の時刻と比較します。両者が一致すると、前回までの 2 つの関数を呼び出します。
AudioContext オブジェクトインスタンスには currentTime プロパティがあり、最初にコンテキストを作成してから何秒経ったかを取得することができます。ステップシーケンサでタイミングをとるために使用します。これは非常に正確で、小数点以下 15 桁程度で正確な浮動小数点値を返します。
draw() 関数も UI を更新するために必要で、そうすれば拍子がいつ進むかを見ることができます。
これで、あとはインストゥルメントを演奏する前にサンプルを読み込むだけです。ファイルが取得され、デコードされると消えるローディング画面を追加します。そして、プレイボタンのクリックイベントを使用してスケジューラーを開始できるようにします。
これでブラウザー内に楽器ができました。これらのテクニックを発展させて、もっと手の込んだものを作成することができます。
This page was last modified on 2025年7月22日 by MDN contributors.
Your blueprint for a better internet.
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998–2026 by individual mozilla.org contributors. Content available under a Creative Commons license.