クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【Tone.js】ドラムパッドにエンベロープ(ADSR)とエフェクトを設定する

Tone.jsの続きです。前回はリズム系のシンセ音でドラムパッドを作りました。今回は、パッドの音色に手を加えたく、エンベロープ設定とエフェクトを加えてみました。それではいきましょう!

【目次】

※参考:【Tone.js】リズム系のシンセ音でドラムパッドを作った - クモのようにコツコツと

※参考:Tone.jsを習得するためにやった事まとめ
qiita.com

前回のおさらい

前回作ってみたドラムパッド

See the Pen tone.js-Drum Pad by イイダリョウ (@i_ryo) on CodePen.

JSコード

//DOM
const kick = document.querySelector('#kick');
const snare = document.querySelector('#snare');
const hihat = document.querySelector('#hihat');

//シンセ生成
const membrane = new Tone.MembraneSynth().toMaster();
const noise = new Tone.NoiseSynth().toMaster();
const metal = new Tone.MetalSynth().toMaster();

kick.addEventListener('click', () => { 
 membrane.triggerAttackRelease('C0','2n'); 
}, false);

snare.addEventListener('click', () => { 
 noise.triggerAttackRelease('8n');  
}, false);

hihat.addEventListener('click', () => { 
 metal.triggerAttackRelease('32n'); 
}, false);

最後のクリックイベントの中のtriggerAttackRelease()の引数で音の長さを指定したが変わらず。それ以外の方法をとる必要がありそう。

詳細は前回の記事を参照。

※参考:【Tone.js】リズム系のシンセ音でドラムパッドを作った - クモのようにコツコツと

エンベロープ(ADSR)を変更する

エンベロープ(ADSR)とは

まずエンベロープ(ADSR)の設定を変更してみる。エンベロープとは音量の立ち上がりや減衰をコントロールする設定。以前こちらの記事でも行った(この時はメロディ再生)。

※参考:【Tone.js】メロディ再生、シンセ音源加工、エフェクター接続(マリオを打ち込んだ!) - クモのようにコツコツと

エンベロープについては以前も紹介した下記の記事がとてもわかりやすい。

f:id:idr_zz:20200427065042j:plain

※参考:AMPをエンベロープ(ADSR)でコントロールする シンセサイザー 初心者講座

A(アタック)、D(ディケイ)、S(サスティーン)、R(リリース)の4つのパラメータがあり、下記の内容を設定できる。

  • A(アタック):立ち上がりから最大音量までにかかる時間
  • D(ディケイ):最大音量から安定音量までにかかる時間
  • S(サスティーン):安定的に楽器が鳴り続ける音量(ピアノでいえば鍵盤を押し続けている間の音量)
  • R(リリース):音がなくなる(減衰)までにかかる時間(ピアノでいえば鍵盤を離したあとの時間)

エンベロープ設定後のドラムパッド

エンベロープを変更してみたものこちら

See the Pen tone.js-Drum Pad-2 by イイダリョウ (@i_ryo) on CodePen.

最初の状態より音の長さが変わった!

JSコード全体

//DOM
const kick = document.querySelector('#kick');
const snare = document.querySelector('#snare');
const hihat = document.querySelector('#hihat');

// エンベロープ(キック)
let optsMembrane = {
  pitchDecay  : 0.001,
  envelope  : {
    attack  : 0.001 ,
    decay  : 1.5 ,
    sustain  : 0.01 ,
    release  : 0.01 
  },
  volume: 25
}

// エンベロープ(スネア)
let optsNoiseSnare = {
  envelope  : {
    attack  : 0.001 ,
    decay  : 0.7 ,
    sustain  : 0
  }
}

// エンベロープ(ハイハット)
let optsNoiseHihat = {
  type  : "brown",
  envelope  : {
    attack  : 0.001 ,
    decay  : 0.03 ,
    sustain  : 0
  }
}

//シンセ生成
const membrane = new Tone.MembraneSynth(optsMembrane).toMaster();
const noise1 = new Tone.NoiseSynth(optsNoiseSnare).toMaster();
const noise2 = new Tone.NoiseSynth(optsNoiseHihat).toMaster();

kick.addEventListener('click', () => { 
 membrane.triggerAttackRelease('C0','2n'); 
}, false);

snare.addEventListener('click', () => { 
 noise1.triggerAttackRelease('8n');  
}, false);

hihat.addEventListener('click', () => { 
 noise2.triggerAttackRelease('32n'); 
}, false);

キックのエンベロープ

まずキック音のエンベロープ

// エンベロープ(キック)
let optsMembrane = {
  pitchDecay  : 0.001,
  envelope  : {
    attack  : 0.001 ,
    decay  : 1.5 ,
    sustain  : 0.01 ,
    release  : 0.01 
  },
  volume: 25
}
  • 変数optsMembraneに連想配列でパラメータ設定
  • pitchDecay0.001
  • envelopeの値は連想配列でattackdecaysustainreleaseがある。
  • attack0.001decay1.5sustain0.01release0.01にする
  • volumeを25に

この項目はキック音に使っている「Tone.MembraneSynth」のオプション値を打ち替えている。

※参考:MembraneSynth

pitchDecay

The amount of time the frequency envelope takes.

訳「周波数エンベロープにかかる時間」

エンベロープは実際にCodePenで音を鳴らしながらいろいろ打ち替えてみたが、特にattackだけは0だと音がならない仕様のようだったので0.001にした。

また、キック音は他の音より音量がちょっと小さいと感じたのでvolumeも設定した。

この設定をシンセに適用するには…

const membrane = new Tone.MembraneSynth(optsMembrane).toMaster();

MembraneSynth()の引数にoptsMembraneを入れる。

スネア音のエンベロープ

同じようにスネア音のエンベロープを設定する。

// エンベロープ(スネア)
let optsNoiseSnare = {
  envelope  : {
    attack  : 0.001 ,
    decay  : 0.7 ,
    sustain  : 0
  }
}
  • 変数optsNoiseSnareに連想配列でパラメータ設定
  • envelopeの値は連想配列でattackdecaysustainがある。
  • attack0.001decay0.7sustain0にする

スネアは「NoiseSynth」の設定値を打ち替えた。(デフォルトでreleaseの設定がなかった)

※参考:https://tonejs.github.io/docs/13.8.25/NoiseSynth

decayの音を伸ばしてみた。

この設定をシンセに適用。

const noise = new Tone.NoiseSynth(optsNoiseSnare).toMaster();

Tone.NoiseSynth()の引数にoptsNoiseSnareを入れる。

ハイハットのエンベロープ

前回ハイハットはMetalSynth()にしていたが、今回、スネアと同じNoiseSynth()にしてみる。

const noise1 = new Tone.NoiseSynth(optsNoiseSnare).toMaster();
const noise2 = new Tone.NoiseSynth().toMaster();
  • スネアのNoiseSynth()の変数名をnoise1とする
  • ハイハットのシンセもNoiseSynth()にして、変数名をnoise2とする。

MetalSynth()の「ピシー」という音はちょっとイメージと違ったため。また、こちらの記事でも触れたようにハイハットもノイズ シンセから作れそうなので、試してみたい。

※参考:【Tone.js】シンセでリズム音を作る方法を調べた - クモのようにコツコツと

ハイハットのエンベロープ設定

// エンベロープ(ハイハット)
let optsNoiseHihat = {
  type  : "brown",
  envelope  : {
    attack  : 0.001 ,
    decay  : 0.03 ,
    sustain  : 0
  }
}
  • 変数optsNoiseHihatに連想配列でパラメータ設定
  • typebrown
  • envelopeの値は連想配列でattackdecaysustainがある。
  • attack0.001decay0.03sustain0にする

スネアと同じく「NoiseSynth」のオプションを打ち替えている。

※参考:https://tonejs.github.io/docs/13.8.25/NoiseSynth

ノイズのタイプはデフォルトはwhiteだが他にpinkbrownがあるようだ。

the noise type (white pink brown)

※参考:Noise

試しにbrownに変えてみたが、正直、そんなに違いは感じなかったw

ハイハットよりもdecayは縮めて0.03にしてみた。

最後に、この設定をシンセに適用する。

const noise2 = new Tone.NoiseSynth(optsNoiseHihat).toMaster();

先程作ったnoise2NoiseSynth()の引数にoptsNoiseHihatを入れる。

これで音の長さが変わった!

エフェクトをかける

エフェクト適用後のドラムパッド

以前のこちらの記事でエンベロープの他にリバーブ(残響音)のエフェクトをかけたが、同様にリズムパッドでもエフェクトをかけられるか試す。

※参考:【Tone.js】メロディ再生、シンセ音源加工、エフェクター接続(マリオを打ち込んだ!) - クモのようにコツコツと

やってみたものこちら。

See the Pen tone.js-Drum Pad-3 by イイダリョウ (@i_ryo) on CodePen.

三つの音色が少し変わった!それぞれに以下のエフェクトをかけている。

  • キック音にピッチシフトをかけて音の高さを下げる
  • スネア音にリバーブをかけて反響を増やす
  • ハイハットにディレイをかけて山彦のように繰り返す
//DOM
const kick = document.querySelector('#kick');
const snare = document.querySelector('#snare');
const hihat = document.querySelector('#hihat');

// エンベロープ(キック)
let optsMembrane = {
  pitchDecay  : 0.001,
  envelope  : {
    attack  : 0.001 ,
    decay  : 1.5 ,
    sustain  : 0.01 ,
    release  : 0.01 
  },
  volume: 25
}

// エンベロープ(スネア)
let optsNoiseSnare = {
  envelope  : {
    attack  : 0.001 ,
    decay  : 0.7 ,
    sustain  : 0
  }
}

// エンベロープ(ハイハット)
let optsNoiseHihat = {
  type  : "pink",
  envelope  : {
    attack  : 0.001 ,
    decay  : 0.03 ,
    sustain  : 0
  }
}


//シンセ生成
const membrane = new Tone.MembraneSynth(optsMembrane).toMaster();
const noise1 = new Tone.NoiseSynth(optsNoiseSnare).toMaster();
const noise2 = new Tone.NoiseSynth(optsNoiseHihat).toMaster();


// エフェクト(ピッチシフト)
const pitchShift = new Tone.PitchShift().toMaster();
pitchShift.pitch = -3;
pitchShift.windowSize = 0.1;
membrane.connect(pitchShift);

// エフェクト(リバーブ)
const reverb = new Tone.Freeverb(0.6,500).toMaster();
noise1.connect(reverb);

// エフェクト(ディレイ)
const delay = new Tone.Delay(0.02).toMaster();
noise2.connect(delay);


kick.addEventListener('click', () => { 
 membrane.triggerAttackRelease('C0','2n'); 
}, false);

snare.addEventListener('click', () => { 
 noise1.triggerAttackRelease('8n');  
}, false);

hihat.addEventListener('click', () => { 
 noise2.triggerAttackRelease('32n'); 
}, false);

キック音のピッチを下げる(PitchShift())

まず最初にやってみたのはキック音のピッチ音を下げること。

キック音を下げたいな、と思ったが前回triggerAttackRelease()C0と打っても変わらず

 membrane.triggerAttackRelease('C0','2n'); 

※参考:【Tone.js】リズム系のシンセ音でドラムパッドを作った - クモのようにコツコツと

エフェクトにピッチシフトがあったので、これで下げてみる。

// エフェクト(ピッチシフト)
const pitchShift = new Tone.PitchShift().toMaster();
pitchShift.pitch = -3;
pitchShift.windowSize = 0.1;
membrane.connect(pitchShift);
  • 変数pitchShiftでエフェクトPitchShift()のインスタンス作成
  • pitchShiftpitch-3
  • pitchShiftwindowSize0.1
  • membraneのシンセにconnect()pitchShiftを繋げる

PitchShift()の設定値はこちらを参照

※参考:PitchShift

pitchの数値は半音階らしく12で1オクターブのようだ。

pitchShift.pitch = -12; //down one octave
pitchShift.pitch = 7; //up a fifth

今回は-3にした。

windowSizeというのはピッチを極端に変えた時の不自然な感じを調整する処理っぽい。

The window size corresponds roughly to the sample length in a looping sampler. Smaller values are desirable for a less noticeable delay time of the pitch shifted signal, but larger values will result in smoother pitch shifting for larger intervals. A nominal range of 0.03 to 0.1 is recommended.

ウィンドウサイズは、ループサンプラーのサンプル長にほぼ対応します。 ピッチシフトされた信号の遅延時間を目立たなくするためには、値が小さいほど望ましいですが、値が大きいほど、間隔が広い場合のピッチシフトがスムーズになります。 0.03〜0.1の公称範囲をお勧めします。

今回は試しに入れてみた感じ。

最後にシンセ音に.connect()メソッドでエフェクトを繋げる。

スネア音にリバーブをかける(Freeverb())

次にスネアにリバーブをかけてみる。

// エフェクト(リバーブ)
const reverb = new Tone.Freeverb(0.6,500).toMaster();
noise1.connect(reverb);
  • 変数reverbでエフェクトFreeverb()のインスタンス作成。
  • Freeverb()の第一引数は部屋の大きさ[ roomSize ]、第二引数は湿気[ dampening ]
  • noise1のシンセにconnect()reverbを繋げる

これは以前の記事と同じエフェクト。お風呂や教会のように反響させる「空間系」のエフェクト。

※参考:【Tone.js】メロディ再生、シンセ音源加工、エフェクター接続(マリオを打ち込んだ!) - クモのようにコツコツと

Freeverb()のオプション値についてはこちらも参照。

※参考:Freeverb

これで少しスネアの音に響きが加わった感じになった。

ハイハットにディレイをかける(Delay())

最後、ハイハットにも何かかけてみる。もう一つの「空間系」のエフェクト、ディレイをかけてみる。ディレイは山彦のように音を繰り返す。

// エフェクト(ディレイ)
const delay = new Tone.Delay(0.02).toMaster();
noise2.connect(delay);
  • 変数delayでエフェクトDelay()のインスタンス作成。
  • Delay()の引数は遅延時間[ delayTime ]
  • noise2のシンセにconnect()delayを繋げる

書き方はリバーブとほとんど一緒だな。

delayTimeは「遅延時間」

The amount of time the incoming signal is delayed.

着信信号が遅延する時間。

これが長いほど時間差が広がる。今回はわずかに遅れて鳴るようにした。

(第二引数に最大遅延時間[ maxDelay ]があったが、こちらの値は変更できないらしいので無視)

※参考:Delay

最後に

f:id:idr_zz:20200427081752j:plain

今回は、過去のメロディ打ち込みの時にやったエンベロープやエフェクト設定をドラムパッドに組み合わせる感じだったけど、これによってエンベロープとエフェクトの書き方、シンセ音への繋げ方の理解が進みました!

次回からはリズム音の打ち込みをやってみたいと思います。最終的にドラムマシンみたいなのを作れたらいいなと。それではまた!


※参考:Tone.jsを習得するためにやった事まとめ
qiita.com