簡易的なディレイ・コーラスエフェクト
再生する
私の
エフェクトなし
ディレイエフェクト
- 120ms後に
- ピッチを1/10半音上げ
- ボリュームを30%から
- 40%ずつ減少した音を再生
- それを2回繰り返す
コーラスエフェクト
- 即座に
- ピッチを1/5半音上げ
- ボリュームを30%から
- 50%減少した音を再生
ソースコード
シーケンサーメソッド
(piano.js
から一部抜粋)
const seqIdAllowFlg = new Set();
/**
* タイミング(リズム)に合わせてコールバック関数を実行する。
*
* @param {Function} callback 1つ前の実行からsequence[i-1]の間待機して実行される関数。この関数には引数としてvalues[i]が渡される。
* @param {Array} sequence 次のcallbackの実行までの待機時間の配列。valuesと要素数が等しい、もしくは要素数-1である必要がある。
* @param {Array} values callbackに渡される値の配列。sequenceと要素数が等しい、もしくは要素数+1である必要がある。
* @param {number} [bpm=240] sequence[]に設定されている値の1を全音符、1/4を1拍とした時のBPM。
* @param {any} [seqId=undefined] 同一のIdは重ねて呼び出しができない。未設定の場合は重複した呼び出しが可能。
*/
function sequencer(callback, sequence, values, bpm = 240, seqId = undefined) {
// sequenceとvaluesは完全に1対1で対応している必要がある
// sequenceの最後の値は意味をなさないため省略できる
if (sequence.length !== values.length && sequence.length + 1 !== values.length) {
throw new Error("The length of sequence and values are did not match");
}
// 同一Idの重複呼び出しをロック
if (seqId != undefined) {
if (seqIdAllowFlg.has(seqId)) {
return;
}
seqIdAllowFlg.add(seqId);
}
// 全てのvalues[i]をsequence[0:i]の遅延で呼び出す
let totalTime = 0;
for (let i = 0; i < values.length; i++) {
// 秒をミリ秒に変換し、BPMを乗する
// 240BPM全音符 = 60BPM4分音符 = 1000ms
const waitTime = sequence[i] ? sequence[i] * 1000 / (bpm / 240) : 0;
const value = values[i];
// sequence[i]以前の全てを足した分だけ待機する
setTimeout(() => {
// 関数呼び出し
callback(value);
}, totalTime);
totalTime += waitTime;
}
// ロックを解放
if (seqId != undefined) {
setTimeout(() => {
seqIdAllowFlg.delete(seqId);
}, totalTime);
}
}
ディレイエフェクト用シーケンサ
/**
* @param {Function} callback 引数の配列の第二引数はvolume
* @param {Array} sequence 通常通り
* @param {Array} values 通常通り
* @param {number} bpm 通常通り
* @param {any} seqId 自動的にプレフィックスが付加される
* @param {number} delayMs 遅延する秒数(ミリ)
* @param {number} pitchShift 一つ前から音程をずらす値(midi)
* @param {number} volume 1番目の音量
* @param {number} decay 一つ前から変化する音量の倍率(1 := 1倍)
* @param {number} overwrap 重ねる回数
*/
function playNoteAllDelaySequencer(
callback,
sequence,
values,
bpm = 240,
seqId = undefined,
delayMs = 0,
pitchShift = 0,
volume = 0.25,
decay = 1,
overwrap = 1
) {
let totalTime = 0;
for (let i = 0; i < overwrap + 1; i++) {
const waitTime = delayMs;
const shiftedValues = values.map((value) => {
return [
value.map((midi) => {
return midi + pitchShift * i;
}),
volume * Math.pow(decay, i)
]
});
const prefixSeqId = seqId != undefined ? `delay${i}-${seqId}` : undefined;
setTimeout(() => {
sequencer(callback, sequence, shiftedValues, bpm, prefixSeqId);
}, totalTime);
totalTime += waitTime;
}
}
音楽パターン
const SONG1_SEQUENCE = [
2 / 24,
1 / 24,
1 / 8,
2 / 24 + 0.02,
1 / 24 + 0.01,
1 / 24 + 0.01,
1.5 / 24 + 0.02,
1 / 20,
1 / 20,
1 / 20,
1.5 / 20,
1 / 16,
1 / 16,
1 / 16,
1 / 24,
2 / 24,
2 / 24,
3 / 28,
4 / 28,
0.5
];
const SONG1_VALUES = [
[41, 60, 65, 69],
[57],
[72],
[46, 53.1, 56.1, 61, 65 - 0.05, 68, 72, 75, 77],
[75],
[73],
[72],
[53, 70],
[60],
[63, 68],
[70],
[48, 58, 63],
[55],
[65],
[49, 56, 60, 63],
[65],
[63],
[39, 46.1, 55, 58],
[63],
[41 - 0.15, 48 - 0.15, 53.05, 57, 60, 65]
];