マガジン

音量を自動で下げる:効果音が鳴るときに音楽を控えめにする

遊びをつくる

執筆にClaudeを使用しています。

つなぎ目のない銅色のリボンとテープリールで、自然なBGMループを表した写真風イメージ。
記事のテーマをもとに制作したAI生成イラスト。

アイテム取得音やクリア音が鳴ったのに、BGMに埋もれて気づけなかった。そこで効果音を大きくすると、今度は音が刺さって耳に痛い。こうした行き詰まりを、BGMの側を一瞬だけ下げることで解く手法が「ダッキング」です。効果音が鳴っている間だけ音楽の音量を自動で下げ、終わったらなめらかに戻します。放送やゲームの音づくりで広く使われている考え方です。 実装は短く、Web Audio APIなら音量の予約を数行書くだけです。難しいのはコードより、どのくらい下げ、どのくらいの速さで戻すと自然に聞こえるかです。下げすぎると音楽が引っ込んで不自然になり、戻りが遅いと次の効果音に間に合わなくなります。 この記事では、そのまま動く最小のコードと、聞こえ方を比べる手順を示します。結果は載せません。ご自身の耳とスピーカーで確かめるための実験設計書です。

この記事の目次
  1. 確かめる問いと、最小の実装
  2. 比べる条件と、手順の組み方
  3. 記録の取り方
  4. 結果の読み方
  5. 起こりやすい誤り
  6. 次の行動
  7. 参考資料

確かめる問いと、最小の実装

確かめる問いは次の二つです。

  1. ダッキングがあると、効果音の聞き取りやすさは、効果音を大きくする方法に比べてどう変わるか。
  2. 下げ量と戻る時間を変えると、音楽の「引っ込み感」はどう変わるか。

準備するものは、Web Audio APIに対応した最新のブラウザ、ヘッドホンまたはスピーカー、紙かメモ帳です。次のコードを index.html として保存し、ブラウザで開きます。外部ライブラリは使わず、通信もしません。BGMの代わりに、ペンタトニックの音を0.3秒ごとにランダムで鳴らします。

html
<button id="bgm">音楽を始める</button>
<button id="sfx">効果音</button>
<label><input type="checkbox" id="on" checked> ダッキング</label>
<label>下げ量 <input type="range" id="depth" min="0" max="1" step="0.05" value="0.3"></label>
<label>戻る時間 <input type="range" id="rel" min="0.05" max="1.5" step="0.05" value="0.4"></label>
<script>
const ctx = new AudioContext();
const music = ctx.createGain();
music.connect(ctx.destination);
const $ = id => document.getElementById(id);
const notes = [262, 294, 330, 392, 440];
function tone(freq, dur, vol, dest, type) {
  const o = ctx.createOscillator(), g = ctx.createGain();
  const t = ctx.currentTime;
  o.type = type; o.frequency.value = freq;
  g.gain.setValueAtTime(vol, t);
  g.gain.exponentialRampToValueAtTime(0.001, t + dur);
  o.connect(g); g.connect(dest);
  o.start(t); o.stop(t + dur);
}
let timer;
$("bgm").onclick = () => {
  ctx.resume();
  if (timer) return;
  timer = setInterval(() => {
    tone(notes[Math.floor(Math.random() * notes.length)], 0.6, 0.2, music, "triangle");
  }, 300);
};
$("sfx").onclick = () => {
  ctx.resume();
  if ($("on").checked) duck();
  tone(880, 0.15, 0.2, ctx.destination, "square");
};
function duck() {
  const g = music.gain, t = ctx.currentTime, now = g.value;
  const depth = Number($("depth").value), rel = Number($("rel").value);
  const atk = 0.03, hold = 0.2;
  g.cancelScheduledValues(t);
  g.setValueAtTime(now, t);
  g.linearRampToValueAtTime(depth, t + atk);
  g.setValueAtTime(depth, t + atk + hold);
  g.linearRampToValueAtTime(1, t + atk + hold + rel);
}
</script>

仕組みは三段階です。効果音の直前に、音楽用のゲインノード(音量を決める部品)を0.03秒かけて「下げ量」の値まで落とします。そのまま0.2秒保ち、設定した「戻る時間」をかけて1に戻します。音楽は music を通り、効果音は通らないので、下がるのは音楽だけです。

比べる条件と、手順の組み方

まず「下げ量」の値を、聞こえる大きさの感覚に置き換えておきます。ゲインは振幅の倍率なので、半分にしても聞こえ方は半分になりません。おおよそ次のとおりです。

  • 0.7、目安(dB):約 -3 dB
  • 0.3、目安(dB):約 -10 dB
  • 0.1、目安(dB):約 -20 dB

次の6条件と1つの対照を用意します。「戻る時間」の単位は秒です。

  • A、ダッキング:なし、下げ量:-、戻る時間:-
  • B、ダッキング:あり、下げ量:0.7、戻る時間:0.4
  • C、ダッキング:あり、下げ量:0.3、戻る時間:0.4
  • D、ダッキング:あり、下げ量:0.1、戻る時間:0.4
  • E、ダッキング:あり、下げ量:0.3、戻る時間:0.1
  • F、ダッキング:あり、下げ量:0.3、戻る時間:1.2
  • G(対照)、ダッキング:なし、下げ量:音楽を常に0.3に固定、戻る時間:-

Gは、ダッキングが本当に必要かを確かめる条件です。チェックを外し、ブラウザの開発者ツールのコンソールに music.gain.value = 0.3 と入力して、音楽を常に小さくします。「音楽を常に小さくする」という単純な方法と比べて、ダッキングに優位があるかを見るためです。

各条件で、次の二つの鳴らし方を試します。

  • 単発:約5秒に1回、効果音を鳴らします。これを5回行います。
  • 連打:約0.5秒間隔で5回続けて鳴らします。これを3セット行います。

手順は次のとおりです。

  1. システムの音量を、BGMだけがはっきり聞こえる程度に決め、実験中は動かしません。
  2. 条件を並べた紙を切って引くなどして、順番をランダムにします。AからGを順に試すと、慣れや疲れの影響が後ろの条件に偏るためです。
  3. 条件を設定し、「音楽を始める」を押して数秒聞いてから、単発、連打の順に鳴らします。
  4. 1条件ごとに評価を記録し、10秒ほど休んでから次へ進みます。

可能であれば、設定する人と聞く人を分けます。聞く人にスライダーやチェックを見せなければ、「ダッキングありのほうが良いはず」という思い込みを減らせます。一人で行うなら、設定後に画面から目を離し、目を閉じて聞くだけでも差が出ます。

記録の取り方

評価は、同じ基準で1〜5点の整数にします。項目は二つに絞ります。

  • 聞き取りやすさ:効果音が鳴ったと即座にわかったか(5が最も聞き取りやすい)。
  • 音楽の自然さ:音楽が引っ込んだこと、戻ってくることが気にならなかったか(5が最も気にならない)。

記録の単位は「1条件・1つの鳴らし方につき1点」です。単発5回は5回の印象をまとめて1点にし、連打は3セットを通して1点にします。条件ごとに、次のような表をメモ帳に作ります。

条件 / 順番 / 単発:聞き取り / 単発:自然さ / 連打:聞き取り / 連打:自然さ / 気づいたこと

「気づいたこと」の欄には、点数にならない違和感を一言で書きます。たとえば、音楽の戻り際に音が膨らんで聞こえる、連打の途中で音楽がずっと小さいままに感じる、などです。点数は人によって基準がずれますが、この欄は後で原因を探すときに役立ちます。

可能なら、別の日、あるいは別の人にも同じ表で評価してもらいます。1回の記録は、その日の耳の状態や機器に左右されます。同じ傾向が繰り返し出たら、その傾向を信頼できます。

結果の読み方

点数が出たら、条件を二つずつ見比べます。判断の目安は次のとおりです。

  • AとC(ダッキングの有無)を比べる:Cのほうが聞き取りやすさが高く、自然さが大きく落ちていなければ、ダッキングが効いています。聞き取りやすさが同程度なら、このBGMと効果音の組み合わせでは、元々音が分かれて聞こえていると考えられます。
  • CとG(常に小さい音楽)を比べる:Gでも聞き取りやすさが十分で、音楽の迫力不足が気にならないなら、ダッキングを入れなくても足ります。Cが勝つのは、音楽の存在感を保ちながら効果音を通したいときです。
  • B、C、Dを比べる:下げ量を深くするほど聞き取りやすさは上がるはずですが、自然さが落ち始める点が上限の目安です。自然さが急に下がる直前の値を採用します。
  • C、E、Fを比べる:Eで戻りの急な音量変化が気になる、またはFで連打中に音楽が小さいままで気になる、といった記録が出たら、その中間の値を探します。

気づいたこと欄に「連打の途中で音楽が戻りきらない」という書き込みが多い場合は、効果音の頻度が高いゲームで戻る時間が長すぎる合図です。戻る時間を短くするか、頻出する効果音のほうはダッキングの対象から外すことを検討します。

起こりやすい誤り

実装と評価で、つまずきやすい点が三つあります。

一つ目は、音が出ないことです。 ブラウザは、ページを開いただけでは音を出さないことがあります。コードではボタンの操作内で ctx.resume() を呼んでいるので、最初に「音楽を始める」か「効果音」を押してください。

二つ目は、連打での音量の飛びです。 ダッキングの途中で次の効果音が来ると、音量の予約が重なります。コードでは、先に現在値を読み、cancelScheduledValues で古い予約を消し、現在値から下げ直しています。この三行を省くと、途中で音量が跳ねる原因になります。ただし、ブラウザによっては予約の途中で読んだ現在値が少しずれることがあります。連打で変な音の動きが聞こえたら、お使いのブラウザの動作として、その点を記録に残してください。

三つ目は、条件を一度に動かすことです。 下げ量と戻る時間を同時に変えると、どちらが印象を変えたのか分からなくなります。表のとおり、1回に動かす値は一つにします。また、音量の基準が変わらないよう、実験中はシステム音量とヘッドホンの位置も動かさないでください。

なお、ここで使った音は単純な波形です。実際のゲームでは、BGMの密度や効果音の高さによって、同じ値でも聞こえ方が変わります。見つけた値は、ご自身のゲームの音で確かめ直すのが確実です。

次の行動

まずコードを保存し、条件AとCだけを、目を閉じて5回ずつ聞き比べてください。違いが分かったら、残りの条件に進みます。最後に、採用した下げ量と戻る時間を、ご自身のゲームの音にそのまま移して聞き直しましょう。

参考資料

  • MDN Web Docs「Web Audio API」「AudioParam」