電話の向こうで工事の音がして、相手の声がところどころかき消される。それでも会話は途切れて聞こえず、声はずっと続いていたように感じる。誰でも身に覚えのあるこの日常は、実は耳の錯覚です。かき消された瞬間、声の証拠は本当に耳へ届いていません。それでも聞こえた気がするのは、脳がその区間を作って埋めているからです。
連続性の錯覚と呼ばれるこの現象は、この連載の「耳のバグ」の棚では差音 ・ミッシング・ファンダメンタル ・シェパードトーン ・リセ・リズム に続く5本目です。これまでの4本が音の高さやテンポの錯覚だったのに対し、今回は「鳴っていない音がまるごと聞こえる」という、いちばん大胆な捏造を耳がやってのけます。
穴は穴に聞こえる。では塞ぐと?
まず錯覚が起きない側から見ます。サイン波のような単純な音を、鳴らして、止めて、また鳴らして、と繰り返すと、当然ながらプツプツと途切れた音に聞こえます。切れ目は100ミリ秒。短いようで、耳には隠しようのない長さです。
ここで、切れ目の区間だけに大きめのノイズをかぶせます。音そのものは1ミリ秒たりとも足していません。トーンは切れ目で完全に止まったまま、穴をノイズで塞いだだけです。ところが聞いてみると、トーンはノイズの向こうで鳴り続けている1本の音に化けます。物理的には存在しない区間の音を、耳がはっきり聞かせてくるのです。
種を明かすと、これは脳が行っている推論の副産物です。ポイントは、かぶせたノイズが「もしトーンが鳴り続けていたとしても、かき消してしまう」だけの大きさを持っていることです。このとき脳の立場からすると、ノイズの区間にトーンの証拠が無いのは当然で、「途切れた」証拠にはなりません。鳴っていても鳴っていなくても、届く音は同じだからです。そこで脳は、切れ目の前後がきれいにつながるという状況証拠から「鳴っていたはず」と判定し、その区間の音を自前で補って聞かせます。逆に言えば、ノイズが小さくて「鳴っていれば聞こえたはず」の状況では、脳は正しく「途切れた」と判定します。錯覚が起きるかどうかの分かれ目は、ノイズの音量が握っているわけです。
触ってみる
切り替えながら確かめられるデモを用意しました。音の高さがゆっくり上下する「うねる音」で、まず「無音のまま」のプツプツ途切れた状態を確認してから、「ノイズで埋める」に切り替えてみてください。それだけで、うねりがノイズの向こうで1本につながります。切れ目の間も音程が動き続けて聞こえたら、それは完全に脳の作った音です。イヤホンで聴くとよく効きます。
うまく表示されないときは、デモを別タブで開く 。
いちばんの見どころはノイズの音量スライダーです。つながって聞こえている状態から少しずつ下げていくと、どこかで錯覚が壊れて、突然「途切れた音+小さいノイズ」に聞こえ出します。先ほどの理屈どおり、「鳴っていれば聞こえたはずなのに聞こえない」と脳が判定できる音量まで下がった瞬間です。「一定の音」モードも用意しましたが、こちらは効き方の個人差がかなり大きい上級編です。動き続ける軌道という強い手がかりを持つうねる音に比べて、一定の音の補完は地味で、同じ音量でも途切れて聞こえる人がいます。錯覚は万人に同じ強さでは働かない、という実感も含めて試してみてください。
この錯覚は、単純な音だけでなく言葉でも起きます。文章の一部の音を咳の音で置き換えて聞かせると、聞き手は欠けた音素まで含めて文章を完全に聞き取り、どこが欠けていたかを言い当てられない。1970年に心理学者リチャード・ウォーレンが報告した音素修復と呼ばれる実験で、連続性の錯覚が日常の聞き取りをずっと下支えしていることを示しました。雑踏や電話越しで会話が成立するのは、耳が物理的な音をそのまま報告する装置ではなく、欠けを埋めながら聞かせる推論装置だからです。
どう実現するか
デモの実装は、この連載でやってきたことの組み合わせで足ります。トーンは鳴らしっぱなしのオシレーターを、ゲイン(音量)の自動化で周期的にオン・オフするだけ。ノイズは2秒ぶんの乱数を詰めたバッファをループ再生して、切れ目の区間だけゲインを上げます。
const PERIOD = 0.53, TONE_ON = 0.43; // 0.43秒鳴らして0.1秒の切れ目
const OVERLAP = 0.04, RAMP = 0.012; // ノイズは切れ目より少し広く覆う
// サイクル k のトーンとノイズの窓
toneGain.gain.setValueAtTime(0, on);
toneGain.gain.linearRampToValueAtTime(TONE_GAIN, on + RAMP);
toneGain.gain.setValueAtTime(TONE_GAIN, off - RAMP);
toneGain.gain.linearRampToValueAtTime(0, off);
noiseGain.gain.setValueAtTime(0, off - OVERLAP); // 切れ目の少し前から
noiseGain.gain.linearRampToValueAtTime(g, off - OVERLAP + RAMP);
ただし、この錯覚には成立条件があって、雑に作ると全く働きません。実は開発中の最初の版では、真っ白なノイズをそのまま被せていて、錯覚が起きませんでした。ノイズは十分うるさく聞こえるのに、トーンは途切れたまま。計算してみると原因は明快で、白色ノイズはエネルギーが全周波数に薄く広がるため、トーン周辺の帯域だけを見るとトーンのほうが18dBも大きく、まるでかき消せていなかったのです。かき消せていない以上、脳は正しく「途切れた」と聞き取ります。そこでノイズをバンドパスフィルターでトーンの帯域へ絞り込み、ゲインを掛けて、帯域内でノイズがトーンを6dB以上上回るよう設計し直しました。帯域はかなり細く(Q=7)絞っています。マスキングに効くのは帯域内の音圧だけで、帯域の外に広がるエネルギーは錯覚に寄与せず、ただうるさいだけだからです。絞るほど、同じマスキング力を小さな総音量で実現できます。錯覚を起こすのは「うるさいノイズ」ではなく「その音の居場所を静かに占拠するノイズ」です。
うねる音では、このバンドパスの中心周波数を音程の軌道に追従させています。ここにも一つ罠があって、バンドパスの帯域幅は中心周波数に比例して広がるため、そのままだと高い音程のときほどノイズの総量が増えて、ノイズ自体の音量がうねって聞こえてしまいます。中心周波数に応じてゲインを補正し、総音量を音程によらず一定に保っています。
細かい数字もふたつ効いています。ひとつは12ミリ秒のランプで、ゲインを瞬間的に切り替えるとプチッというクリックノイズが出て、そこが「切れ目の証拠」として耳に残ってしまいます。もうひとつはノイズの窓を切れ目の前後に40ミリ秒ずつ、合わせて80ミリ秒だけ広く取っている点です。トーンの端とノイズの端がぴったり同時だと、切り替わりの縁が聞き取れてしまう。ノイズが先に立ち上がってからトーンが消えるようにして、縁を完全に覆い隠します。錯覚の実験装置は、こういう縁の処理が生命線です。
ちなみに、この錯覚は音声技術の側でも実用されています。インターネット通話で音声データの一部が届かなかったとき、欠けた区間を前後から予測した音で埋めるパケットロス隠蔽という処理は、脳が勝手にやっている補完を機械側で肩代わりする発想です。埋め方が多少雑でも、聞き手の脳がさらに上から補正してくれるので、思いのほか気づかれません。
参考文献
- R. M. Warren, “Perceptual Restoration of Missing Speech Sounds,” Science, vol. 167 (1970), pp. 392–393. DOI: 10.1126/science.167.3917.392 。咳で置き換えた音素が補完されて聞こえる音素修復の原論文。
- 連続性の錯覚の全体像は Wikipedia: Continuity illusion がまとまっています。
- デモの実装には Web Audio API を使用。GainNode のオートメーションでトーンとノイズの窓を予約しています。
音が消えている100ミリ秒のあいだ、耳に聞こえているものの正体を、ぜひデモで確かめてみてください。
連載「サウンドプログラミング」
← 前回 オートチューンの仕組み
