クモのようにコツコツと

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

【Tone.js】Tone.Part()で細かいタイミングのエイトビートを鳴らす

Tone.jsの続きです。前回Tone.Loop()を使ってエイトビートを鳴らしました。ただ、この方法だと細かいタイミングでリズムを変えることが出来なそうなので、今回は以前、マリオのゲームオーバー音の時に使ったTone.Part()で打ち込んでみました。予想通り細かいタイミングで打ち込みができました。それではいきましょう!

【目次】

※参考:【Tone.js】Tone.Loop()でエイトビートを鳴らす - クモのようにコツコツと

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

前回のおさらい

前回作ったものこちら。ボタンを押すとエイトビートが鳴る。

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

※音が鳴らない場合:CodePen - tone.js-DrumBeat_1

リズム(エイトビート)を鳴らしている主要部

// シンセ実行
const kick = () => {
  membrane.triggerAttackRelease('C0','2n'); 
};

const snare = () => {
  noise1.triggerAttackRelease('8n');
};

const hihat = () => {
  noise2.triggerAttackRelease('32n');
};

// ループ設定
let count = 1;
var loop = new Tone.Loop((time) => {
  if(count === 1 || count === 5){
    kick();
    hihat();
  } else if(count === 3 || count === 7){
    snare();
    hihat();
  } else {
    hihat();
  }

  if(count === 8){
    count = 1;
  } else {
    count++;
  }
}, '8n').start(0);

Tone.Loop()を使ってfor文でカウントを繰り返しながら再生している。この方法だと8拍子にジャストな音しか出せない。

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

※参考:【Tone.js】Tone.Loop()でエイトビートを鳴らす - クモのようにコツコツと

Tone.Part()でエイトビートを鳴らす。

Tone.Part()を使った完成品

メロディ(マリオのゲームオーバ音)打ち込みの時に使ったTone.Part()、こちらの方が細かいタイミングで打ち込みできた。

※参考:【Tone.js】Tone.Part()でメロディのタイミング、音の長さ、音量を調節する(マリオのゲームオーバー音) - クモのようにコツコツと

Tone.Part()を使ってみたもの

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

※音が鳴らない場合:CodePen - tone.js-DrumBeat_4

ボタンを押した時になるエイトビートは前回と変わらない。

リズム設定

キックのリズム

let kickRhythm = [
  ['0:0:0'],
  ['0:2:0'],
];
  • 変数kickRhythmの値は配列で、配列の値もまた配列
  • 配列1つ目の値は0:0:0
  • 配列2つ目の値は0:2:0

コロンで挟まれている3つの数値(0:0:0など)、一つ目は「小節」、二つ目は「音符」、三つ目は音符をさらに細かく微調整。色々打ち替えてみたが02(0からのカウントで1、3番目)ということで4拍子ベースになっている。

次、スネアのリズム

let snareRhythm = [
  ['0:1:0'],
  ['0:3:0'],
];
  • 変数snareRhythmの値は配列で、配列の値もまた配列
  • 配列1つ目の値は0:1:0
  • 配列2つ目の値は0:3:0

スネアはキックの裏拍(バックビート)のアクセントなので13(0からのカウントで2、4番目)になる。

最後、ハイハットのリズム

let hihatRhythm = [
  ['0:0:0'],
  ['0:0.5:0'],
  ['0:1:0'],
  ['0:1.5:0'],
  ['0:2:0'],
  ['0:2.5:0'],
  ['0:3:0'],
  ['0:3.5:0'],
];
  • 変数hihatRhythmの値は配列で、配列の値もまた配列
  • 配列の音符部は上から00.511.522.533.5

4拍子ベースのためエイトビート(8拍子)は0.5刻みになる。

リズムをシンセに設定

Tone.Part()を使ってシンセにリズムを設定する。

// シンセにリズムを設定
let kickPart = new Tone.Part(kick, kickRhythm).start();
let snarePart = new Tone.Part(snare, snareRhythm).start()
let hihatPart = new Tone.Part(hihat, hihatRhythm).start();
  • 変数kickPartTone.Part()のインスタンス作成。引数は左からkickkickRhythmstart()メソッド実行。
  • 変数snarePartTone.Part()のインスタンス作成。引数は左からsnaresnareRhythmstart()メソッド実行。
  • 変数hihatPartTone.Part()のインスタンス作成。引数は左からhihathihatRhythmstart()メソッド実行。

ループ設定

最後にループ設定。

// ループ設定
kickPart.loop = true;
snarePart.loop = true;
hihatPart.loop = true;
  • kickPartsnareParthihatPartともにloop設定をtrue

loop設定の初期値はfalseで1回しか鳴らなかった。trueにすると無限ループになる。(数値だと回数分ループする)

※参考:Part

JSコード全体

JSコードだとこうなる。

// DOM
const beat = document.querySelector('#beat');

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

// エンベロープ(スネア)
let optsNoiseSnare = {
  envelope: {
    attack: 0.001 ,
    decay: 0.5 ,
    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();

// シンセ実行
const kick = () => {
  membrane.triggerAttackRelease('C0','2n'); 
};

const snare = () => {
  noise1.triggerAttackRelease('8n');
};

const hihat = () => {
  noise2.triggerAttackRelease('32n');
};

//リズム設定
let kickRhythm = [
  ['0:0:0'],
  ['0:2:0'],
];

let snareRhythm = [
  ['0:1:0'],
  ['0:3:0'],
];

let hihatRhythm = [
  ['0:0:0'],
  ['0:0.5:0'],
  ['0:1:0'],
  ['0:1.5:0'],
  ['0:2:0'],
  ['0:2.5:0'],
  ['0:3:0'],
  ['0:3.5:0'],
];

// シンセにリズムを設定
let kickPart = new Tone.Part(kick, kickRhythm).start();
let snarePart = new Tone.Part(snare, snareRhythm).start()
let hihatPart = new Tone.Part(hihat, hihatRhythm).start();

// ループ設定
kickPart.loop = true;
snarePart.loop = true;
hihatPart.loop = true;

// BPM設定
Tone.Transport.bpm.value = 90;

// クリックイベント
beat.addEventListener('click', () => {
  Tone.Transport.toggle();
}, false);

エイトビートを複雑なビートにしてみる

エイトビートその1(ドン|タン|ドド|タン)

前回もやったやつ。

「ドン|タン|ドド|タン」という音が鳴る。

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

※音が鳴らない場合:CodePen - tone.js-DrumBeat_5

キック音

let kickRhythm = [
  ['0:0:0'],
  ['0:2:0'],
  ['0:2.5:0'],
];
  • 変数kickRhythmに音符2.5を追加。

エイトビートその2(ドン|タド|ドン|タン)

これも前回やったやつ。

「ドン|タド|ドン|タン」という音が鳴る。

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

※音が鳴らない場合:CodePen - tone.js-DrumBeat_6

キック音

//リズム設定
let kickRhythm = [
  ['0:0:0'],
  ['0:1.5:0'],
  ['0:2:0'],
];
  • 変数kickRhythmに音符1.5を追加。

エイトビートその3(ドンッド|タッタッ|ッドン|タドン)

文字にするとわけわかんないけどw とにかく鳴らしてみてくだせい。

「ドンッド|タッタッ|ッドン|タドン」という音が鳴る

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

※音が鳴らない場合:CodePen - tone.js-DrumBeat_7

キック音

let kickRhythm = [
  ['0:0:0'],
  ['0:0.75:0'],
  ['0:2.5:0'],
  ['0:3.25:0'],
];
  • 変数kickRhythmの音符を00.752.53.25

次、スネア部分

let snareRhythm = [
  ['0:1:0'],
  ['0:1.75:0'],
  ['0:3:0'],
];
  • 変数snareRhythmの音符を11.753に(1.75)を追加

0.25刻みの音符を入れることで16分音符のタイミングも指定できた。

ただ、あまり発生音が近接するとなんか乱れた音になるようだ。なんとかしたい。

最後に

f:id:idr_zz:20200524153015j:plain

ということでTone.Part()を使って細かいタイミングのエイトビートを鳴らすことができました。前回のfor文と違って楽器ごとに細かい数値で指定できるので打ち込みやすくなりました♪

ちょっと気になるのは音が近接した時の音の乱れです。これまでも全く同じタイミングで鳴らすとハイハットの音が聞き取りにくいなどは感じてましたが、想定どおりに再生をコントロールするには対策を調べた方が良さそうです。

次回はTone.Part()を使ってさらに色々なリズムを鳴らしてみたく思います。おそらく変拍子やシャッフル系のリズムもいけるんじゃないかと思っています。それではまた!


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