本文へスキップ

サウンド ​

ファイルから効果音を鳴らし、音楽をストリーミングし、plonk で音楽と効果音をコードで書き、プレイヤーが音量を決められるようにします。すべて @debuffjp/audio の Audio 1 つを通るので、音量の設定は 1 か所で全体に効きます。

ts
import { Audio } from "@debuffjp/audio";

const audio = new Audio(); // ゲームに 1 つ

ブラウザは、プレイヤーがクリックやタップ、キー入力をするまでページの音を出しません。Audio は最初の入力で自動的に音を出せる状態になるので、自分で処理する必要はありません。音を出せるようになると audio.unlocked が true になります。たいていのゲームは、キーかクリックを待つタイトル画面があれば十分です。

サウンドファイル ​

ファイルはほかのアセットと同じく assets/ に置いて読み込み、それぞれ一度だけデコードします。

ts
import { Assets } from "@debuffjp/assets";

const assets = new Assets(await (await fetch("assets/manifest.json")).json());
await assets.load("default");
await audio.decode("coin", assets.audio("coin")); // assets/coin.ogg

audio.play("coin");
audio.play("coin", { volume: 0.8, rateJitter: 0.05 });
オプション内容
volumeこの音の音量。デフォルトは 1 です。
rate再生速度。1 で元の音程、2 で 1 オクターブ上です。
rateJitter再生のたびに rate へランダムに足し引きする量。0.05 にすると、何度も聞く音が毎回まったく同じにはなりません。
pan左右の位置。-1(左)から 1(右)までです。
loop止めるまで繰り返します。
bus"sfx"(デフォルト)か "music"。音量を参照してください。

play はボイスを返します。ループする音を止めるときに使います。フェードの秒数も指定できます。

ts
const engine = audio.play("engine", { loop: true, volume: 0.4 });
engine.stop(0.3);

デコードしていない ID で play を呼ぶとエラーになり、足りない decode の呼び出しを教えてくれます。

長い曲 ​

音楽や環境音のファイルは大きく、decode は音全体をメモリに持ちます。stream は代わりに <audio> 要素で再生するので、すぐに始まり、スマートフォンでも軽く済みます。

ts
const music = audio.stream(assets.url("music/theme"), { volume: 0.7 }); // music バス、デフォルトでループ

music.volume(0.3, 1); // 1 秒かけて 0.3 まで下げる(会話ウィンドウの間など)
music.pause();
music.resume();
music.stop(2); // 2 秒かけてフェードアウト

長い曲は、assets.load() で読み込まないフォルダ(assets/music/ など)へ置きましょう。同じファイルを二度ダウンロードせずに済みます。assets.url はファイルを取得せずにアドレスだけを返します。最初のクリックやキー入力より前に始めたストリームは、その入力で再生が始まります。

コードで作る音楽と効果音 ​

plonk は曲を数行のデータとして書き、その場で合成するので、音声ファイルは要りません。効果音は曲の調で鳴るので、音階を上っていくコンボも音楽とぶつからずになじみます。npm install plonkjs で入れたら、ゲームの音量に従うように music バスへつなぎます。

ts
import { Player } from "plonkjs";
import { theme } from "./theme"; // plonk の Song

const player = new Player({ context: audio.ctx, destination: audio.bus("music") });
player.play(theme, { fade: 1 });

const POP = { wave: "sine", drop: 7, decay: 0.12, sustain: 0, volume: 0.3 } as const;
player.sfx(POP, { degree: 5 }); // 曲の音階の 5 番目の音

0.24.2 から、ゲームに音が必要で、ほかのツールを指定していなければ、コーディングエージェントは plonk を使います(サウンドを作る)。

仮の効果音 ​

audio.synth は、オシレーター 1 つと音程のスライドで短いビープ音やザップ音、打撃音を作ります。まだ音がないゲームで、イベントが起きているかを耳で確かめるために使い、あとでファイルか plonk の効果音に置き換えましょう。

ts
audio.synth({ freq: 660, freqEnd: 220, decay: 0.15 }); // 下がっていく音
audio.synth({ wave: "noise", freq: 400, freqEnd: 80, decay: 0.3, volume: 0.2 }); // 砕ける音
オプション内容
freq、freqEnd始まりと終わりの音程(Hz)。
attack、decayフェードインとフェードアウトの秒数。
wave"square"(デフォルト)、"sine"、"triangle"、"sawtooth"、"noise"。
volume、busplay と同じです。volume のデフォルトは 0.3 です。

音量 ​

音は "sfx" と "music" の 2 つのバスを通り、どちらも "master" に流れます。どれも 0 から 1 で設定できます。変化はなめらかにつながるので、スライダーを動かしてもプツッという音は出ません。

ts
audio.volume("music", 0.5);
audio.volume("master", muted ? 0 : 1); // ミュートは音楽と効果音の両方に効く

プレイヤーの設定は保存して、再読み込みしても残るようにしましょう。platformer テンプレートでは次のようにしています。

ts
import { Storage } from "@debuffjp/core";

const save = new Storage("my-game", { volume: 10 });
audio.volume("master", save.get("volume") / 10);

function setVolume(v: number) {
  save.set("volume", Math.max(0, Math.min(10, v)));
  audio.volume("master", save.get("volume") / 10);
}

自分の Web Audio のノードをゲームの音量に従わせるには、audio.bus("sfx") か audio.bus("music") につなぎます。audio.ctx は AudioContext で、audio.buffer(id) はデコード済みの音を返すので、自分のノードで再生できます。

空間の中の音 ​

ある場所から聞こえ、遠いほど小さく、壁の向こうではこもる音には Earshot を使いましょう。debuff で Earshot を使うでは、タイルマップから壁を作り、sfx バスで再生します。

テスト ​

debuff test はブラウザなしでゲームを実行するので、音は聞こえません。音声の呼び出しは受け付けられ、何もしません。音が鳴ることをテストするには、音を鳴らすきっかけになるゲームの状態を確かめます。スピーカーなしで音楽を確かめたいときは、plonk が曲の音符をデータとして一覧にします(スピーカーなしで音楽をテストする)。良い音かどうかは、やはり耳で判断しましょう。

API リファレンス