コーラス・フランジャーとは?

コーラスは音に揺らぎを与えるエフェクトです. 合唱では, どんなに歌唱力の高い人が集まって歌っても多少なりともピッチのずれは生じてしまいます. しかし, この微妙なずれが合唱らしさを生み出している要因でもあります. コーラスでは, この微妙なずれ, すなわち, 揺らぎを信号処理を駆使して再現するエフェクトです.

フランジャーはジェット機のエンジン音のように, 音に強烈なうねりを与えるエフェクトです.

エフェクトとしては, コーラスとフランジャーはかなり感じが異なりますが, その原理はまったく同じです. 具体的には, ディレイタイム (遅延時間) を周期的に変化させることによって, FM変調を実現しているという点です. 原理が同じであるにも関わらず, まるで異なるエフェクトなのは, パラメータの設定値やフィードバックの有無が影響しています.

コーラス・フランジャーはディレイが基本となっているので, ディレイの実装がよくわからない…という場合は, ディレイ・リバーブのページで簡単でいいので理解してみてください.

FM変調に関してはのちほど解説することにして, まずは実装の解説からします.

コーラス

コーラスは, ディレイタイムを周期的に変化させたエフェクト音を原音とミックスすることにより実装できます. つまり, コーラスの実装は以下の2つ処理に分解可能となります.

ディレイタイムを周期的に変化させる
LFOをAudioParamインスタンスであるDelayNodeインスタンスのdelayTimeプロパティに接続する
原音とエフェクト音をミックスする
GainNodeインスタンスを利用して, Dry / Wet機能を実装する

処理を整理してみると, イントロダクションのページや, ディレイ・リバーブのページで既に解説・実装済のことだけでコーラスを実装できます.

まずは, 原音とエフェクト音のための接続を実装しておきます.

コーラス・フランジャーではディレイタイムを1 sec以上に設定することはまずないので, createDelayメソッドの引数はデフォルト値 (1) のままでいいでしょう.

サンプルコード 01


window.AudioContext = window.AudioContext || window.webkitAudioContext;

// Create the instance of AudioContext
var context = new AudioContext();

// for legacy browsers
context.createDelay = context.createDelay || context.createDelayNode;

// Create the instance of DelayNode
var delay = context.createDelay();

// Create the instance of OscillatorNode
var oscillator = context.createOscillator();  // for Input

// for legacy browsers
oscillator.start = oscillator.start || oscillator.noteOn;
oscillator.stop  = oscillator.stop  || oscillator.noteOff;

// for legacy browsers
context.createGain = context.createGain || context.createGainNode;

// Connect nodes for original sound
// OscillatorNode (Input) -> AudioDestinationNode (Output)
oscillator.connect(context.destination);

// Connect nodes for effect (Chorus) sound
// OscillatorNode (Input) -> DelayNode (Delay) -> AudioDestinationNode (Output)
oscillator.connect(delay);
delay.connect(context.destination);

原音とエフェクト音の接続

図2 - 3 - a. 原音とエフェクト音の接続

次に, LFOをAudioParamインスタンスであるDelayNodeインスタンスのdelayTimeプロパティに接続します. LFOの出力を調整できるように, GainNodeインスタンス (Depth) を接続する点にも着目してください.

サンプルコード 02


/*
 * Add code to sample code 01
 */

// ....

// Create the instance of OscillatorNode
var lfo = context.createOscillator();  // for LFO

// for legacy browsers
lfo.start = lfo.start || lfo.noteOn;
lfo.stop  = lfo.stop  || lfo.noteOff;

// Create the instance of GainNode
var depth = context.createGain();  // for LFO

// Connect nodes for LFO that changes Delay Time periodically
// OscillatorNode (LFO) -> GainNode (Depth) -> delayTime (AudioParam)
lfo.connect(depth);
depth.connect(delay.delayTime);

LFOの接続

図2 - 3 - b. LFOの接続

あとは, 原音とエフェクト音のミックスを調整できるようにGainNodeインスタンスを接続するします. ちなみに, ディレイ・リバーブのように, 原音とエフェクト音の接続それぞれに対して, GainNodeインスタンスを接続するという実装もありですが, 一般的に, コーラス・フランジャーでは, 原音のゲインを常に1として, エフェクト音のゲインのみを調整することが多いので, サンプルコードでは, エフェクト音のみゲイン調整ができるようにします.

そして, サウンドの開始と同時に, LFOのOscillatorNodeインスタンスもstartメソッドを実行します.

サンプルコード 03


/*
 * sample code 01
 */

// ....

// Create the instance of GainNode
var mix = context.createGain();  // for effect (Chorus) sound

// Connect nodes for effect (Chorus) sound
// OscillatorNode (Input) -> DelayNode (Delay) -> GainNode (Mix) -> AudioDestinationNode (Output)
oscillator.connect(delay);
delay.connect(mix);
mix.connect(context.destination);

// sample code 02 ....

// Start sound
oscillator.start(0);

// Effector (Chorus) ON
lfo.start(0);

// Set Base Value
delay.delayTime.value  = 0.020;  // 20 msec (Base Value)

// Set Depth
depth.gain.value = 0.010;  // 20 msec +- 10 msec

// Set Rate
lfo.frequency.value = 0.05;  // 0.05 Hz

// Set Mix
mix.gain.value = 0.5;

エフェクト音の調整

図2 - 3 - c. エフェクト音の調整

また, コーラス・フランジャーのように原音とエフェクト音をミックスするエフェクターは, センド・リターン型エフェクトと呼ばれます. 逆に, エフェクト音のみを出力するエフェクター (コンプレッサーやビブラートなど) はインサート型エフェクトと呼ばれます.

ところで, Depthを実際に変化する値で指定してしまうと, 変化のベースとなる値より大きくならないように常にコントロールしなければならないため, 実装面でも, 利用面でも少々めんどうです. ベース値をハードコーディングしてしまえば解決しますが, エフェクターとしての自由度は下がってしまいます.

そこで, 実際の変化量を指定するのではなく, ベースとなる値に対するDepthの割合を格納する変数を追加して, その値 (Depthの割合) とベース値から実際のDepthを算出します.

サンプルコード 04


/*
 * Add code to sample code 01
 */

// This value must be less than or equal to 1.0 (100%)
var depthRate = 0.5;  // 50 %

// Set Base Value
delay.delayTime.value  = 0.020;  // 20 msec (Base Value)

// Set Depth
depth.gain.value = delay.delayTime.value * depthRate;  // 20 msec +- 10 (20 * 0.5) msec

// ....

最後に, 整理したコードを記載しておきます.

サンプルコード 05


window.AudioContext = window.AudioContext || window.webkitAudioContext;

// Create the instance of AudioContext
var context = new AudioContext();

// for legacy browsers
context.createDelay = context.createDelay || context.createDelayNode;

// Create the instance of DelayNode
var delay = context.createDelay();

// Create the instance of OscillatorNode
var oscillator = context.createOscillator();  // for Input
var lfo        = context.createOscillator();  // for LFO

// for legacy browsers
oscillator.start = oscillator.start || oscillator.noteOn;
oscillator.stop  = oscillator.stop  || oscillator.noteOff;
lfo.start        = lfo.start        || lfo.noteOn;
lfo.stop         = lfo.stop         || lfo.noteOff;

// for legacy browsers
context.createGain = context.createGain || context.createGainNode;

// Create the instance of GainNode
var mix   = context.createGain();  // for effect (Chorus) sound
var depth = context.createGain();  // for LFO

// Connect nodes for original sound
// OscillatorNode (Input) -> AudioDestinationNode (Output)
oscillator.connect(context.destination);

// Connect nodes for effect (Chorus) sound
// OscillatorNode (Input) -> DelayNode (Delay) -> GainNode (Mix) -> AudioDestinationNode (Output)
oscillator.connect(delay);
delay.connect(mix);
mix.connect(context.destination);

// Connect nodes for LFO that changes Delay Time periodically
// OscillatorNode (LFO) -> GainNode (Depth) -> delayTime (AudioParam)
lfo.connect(depth);
depth.connect(delay.delayTime);

// This value must be less than or equal to 1.0 (100%)
var depthRate = 0.5;  // 50 %

// Set Base Value
delay.delayTime.value  = 0.020;  // 20 msec (Base Value)

// Set Depth
depth.gain.value = delay.delayTime.value * depthRate;  // 20 msec +- 10 (20 * 0.5) msec

// Set Rate
lfo.frequency.value = 0.05;  // 0.05 Hz

// Set Mix
mix.gain.value = 0.5;

// Start sound
oscillator.start(0);

// Effector (Chorus) ON
lfo.start(0);

                        
コーラスのノード接続

図2 - 3 - d. コーラスのノード接続

以上でコーラスが完成しました. さっそく, デモ 06・デモ 07で試してみてください. 基本となるディレイタイムと, LFOのDepth / Rate, エフェクト音のMixを組み合わせてお好みのコーラスを発見してください. コーラスは, ディレイタイムを長く (20 - 30 msecぐらい), Rateを低めに (1 Hz以下) に設定することで, 穏やかにエフェクトを与えることが多いです.

デモ 06 (サウンド)

デモ 07 (オーディオ)

フランジャー

フランジャーはコーラスと同じ原理なので, ディレイタイム (遅延時間) が周期的に変化するように実装すればOKです. したがって, コーラスが実装できていれば, フランジャーの実装もほぼ完成しています. コーラスとの実装の違いは, エフェクト音のフィードバックがあることです.

したがって, フランジャーの実装は以下の3つ処理に分解可能となります.

ディレイタイムを周期的に変化させる
LFOをAudioParamインスタンスであるDelayNodeインスタンスのdelayTimeプロパティに接続する
原音とエフェクト音をミックスする
GainNodeインスタンスを利用して, Dry / Wet機能を実装する
エフェクト音をフィードバックする
GainNodeインスタンスを利用して, エフェクト音の出力を入力に戻す

上記の2つの処理は既にコーラスで実装済です. したがって, あとは, エフェクト音のフィードバックの実装が必要です. もっとも, エフェクト音のフィードバックに関しては, ディレイ・リバーブのページで解説・実装済です.

では, コーラスの実装にフィードバックの実装を追加します.

フィードバックの追加

図2 - 3 - e. フィードバックの追加

サンプルコード 06


window.AudioContext = window.AudioContext || window.webkitAudioContext;

// Create the instance of AudioContext
var context = new AudioContext();

// for legacy browsers
context.createDelay = context.createDelay || context.createDelayNode;

// Create the instance of DelayNode
var delay = context.createDelay();

// Create the instance of OscillatorNode
var oscillator = context.createOscillator();  // for Input
var lfo        = context.createOscillator();  // for LFO

// for legacy browsers
oscillator.start = oscillator.start || oscillator.noteOn;
oscillator.stop  = oscillator.stop  || oscillator.noteOff;
lfo.start        = lfo.start        || lfo.noteOn;
lfo.stop         = lfo.stop         || lfo.noteOff;

// for legacy browsers
context.createGain = context.createGain || context.createGainNode;

// Create the instance of GainNode
var mix      = context.createGain();  // for effect (Flanger) sound
var depth    = context.createGain();  // for LFO
var feedback = context.createGain();  // for Feedback

// Connect nodes for original sound
// OscillatorNode (Input) -> AudioDestinationNode (Output)
oscillator.connect(context.destination);

// Connect nodes for effect (Flanger) sound
// OscillatorNode (Input) -> DelayNode (Delay) -> GainNode (Mix) -> AudioDestinationNode (Output)
oscillator.connect(delay);
delay.connect(mix);
mix.connect(context.destination);

// Connect nodes for LFO that changes Delay Time periodically
// OscillatorNode (LFO) -> GainNode (Depth) -> delayTime (AudioParam)
lfo.connect(depth);
depth.connect(delay.delayTime);

// Connect nodes for Feedback
// (OscillatorNode (Input) ->) DelayNode (Delay) -> GainNode (Feedback) -> DelayNode (Delay) -> ...
delay.connect(feedback);
feedback.connect(delay);

// This value must be less than or equal to 1.0 (100%)
var depthRate = 0.8;  // 80 %

// Set Base Value
delay.delayTime.value  = 0.005; // 5 msec (Base Value)

// Set Depth
depth.gain.value = delay.delayTime.value * depthRate;  // 5 msec +- 4 (5 * 0.8) msec

// Set Rate
lfo.frequency.value = 5;  // 5 Hz

// Set Mix
mix.gain.value = 0.9;

// Set Feedback
feedback.gain.value = 0.9;

// Start sound
oscillator.start(0);

// Effector (Flanger) ON
lfo.start(0);

フランジャーのノード接続

図2 - 3 - f. フランジャーのノード接続

以上でフランジャーが完成しました. デモ 08・デモ 09で試してみてください. 基本となるディレイタイムと, LFOのDepth / Rate, エフェクト音のMix, Feedbackを組み合わせてお好みのフランジャーを発見してください. フランジャーは, ディレイタイムを短く (1 - 5 msecぐらい), Mix, Feedbackを大きく設定することで, コーラスと異なり, ダイナミックにエフェクトを与えることが多いです.

デモ 08 (サウンド)

デモ 09 (オーディオ)

FM変調

このセクションでは, コーラス・フランジャーの原理を簡単に解説します. とりあえず, コーラス・フランジャーが実装できればOKという場合はスルーしてください.

コーラス・フランジャーは, FM変調が原理となっています (FMとは, Frequency Modulationの略です). 日本語に翻訳すると周波数変調ということになります.

例えば, 正弦波 (sin波) をFM変調すると図2 - 3 - gのようになります. 時間経過とともに波の構成 (山1つ, 谷1つ) が短くなったり, 長くなったりしている点に着目してみてください.

FM変調

図2 - 3 - g. FM変調

つまり, FM変調とは時間経過とともに周波数が変化する現象のことです.

ビブラート

実は, FM変調をそのまま利用したエフェクトがビブラートです. イントロダクションのページデモ 01を試してもらうとわかると思いますが, ビブラートをかけた音は時間経過とともに周波数が変動しています.

そして, このビブラートをかけたエフェクト音を原音とミックスしたエフェクトがコーラス・フランジャーというわけです. 言い換えれば, コーラス・フランジャーはビブラートが原理となっているエフェクトということです.

ちなみに, コーラス・フランジャーが原音とエフェクト音をミックスするセンド・リターン型のエフェクトなのに対して, ビブラートはエフェクト音のみを出力するインサート型のエフェクトです.

ところで, デモ 01でビブラートを実装したときは, OscillatorNodeインスタンスのfrequencyプロパティにLFOを接続することで, 周波数変調 (FM変調) を実装しました. そのアプローチでコーラス・フランジャーを実装するということも考えられますが, これまで解説した実装では, いずれもディレイタイムを周期的に変化させることによって, FM変調 (ビブラート) をかけたエフェクト音を生成していました.

この理由は, ディレイタイムを周期的に変化させる実装が, より汎用的なFM変調 (ビブラート) の実装だからです. デモ 01のように音源が基本波形のような, 単純なサウンドをFM変調させるのであればどちらの実装でもOKです. しかしながら, 楽器音やオーディオは様々な周波数の波を合成した音なので, ピンポイントで周波数を変調させるということが困難です. そこで, ディレイタイムを周期的に変化させることによって, 楽器音やオーディオに対してもFM変調 (ビブラート) を実現します.

ちなみに, ディレイタイムの周期的な変化が, なぜ, 周波数を変調させることになるのか?を解説すると, ちょっと大変なので, とりあえずは, ディレイタイムの周期的な変化が周波数を変調させるということを理解してもらえれば十分です.

以上のことから, デモ 01のビブラートをディレイタイムを周期的に変化させることによって実装することも可能であり, また, コーラス・フランジャーの実装をエフェクト音のみの出力に変更すれば, (汎用的な) ビブラートを実装することも可能です.

ビブラートのノード接続

図2 - 3 - g. ビブラートのノード接続

インサート型エフェクトなので, エフェクト音のみが出力される接続となっていることに着目してください.

コーラス・フランジャー まとめ

このページでは, コーラス・フランジャーの実装とその原理の概要を解説しました. ディレイの基本実装, つまり, 原音とエフェクト音のミックス, Dry / Wetコントロールの接続, フィードバック, DelayNodeインスタンスのdelayTimeプロパティへの接続 (LFO) など, これまで解説してきたノード接続をフルに駆使する実装だったので, コーラス・フランジャー実装のエッセンスをまとめておきます.

表2 - 3 - a. ノード接続
EffectorConnection
コーラス
原音
入力ノード + AudioDestinationNode
エフェクト音
入力ノード + DelayNode + Mix (GainNode) + AudioDestinationNode
LFO
OscillatorNode + Depth (GainNode) + delayTime (DelayNode)
フランジャー
原音
入力ノード + AudioDestinationNode
エフェクト音
入力ノード + DelayNode + Mix (GainNode) + AudioDestinationNode
LFO
OscillatorNode + Depth (GainNode) + delayTime (DelayNode)
フィードバック
DelayNode + Feedback (GainNode) + DelayNode + ...

コーラス・フランジャーの原理は, FM変調, すなわち, ビブラートであること, そして, ディレイタイムを周期的に変化させることによって, 周波数を変調させているということが, 原理と実装の要点です.