Metronome.fm のパターン(拍のクリックとは別に、1 拍の中で鳴らすガイド音)の種類を増やしました。5 連符・6 連符・7 連符・32 分音符を選べるようになり、ポリリズムは 3 対 2 だけでなく 4 対 3 や 7 対 4 のような比を選べます。8 分音符(8th)と 16 分音符(16th)のパターンでは、スウィングの量(跳ね方の強さ)を変えられます。

- パターンの「編集」。パターンの欄に出すものを選ぶ
- 新しく選べるようになった 5 連符・6 連符・7 連符・32 分音符
この記事では、ポリリズムの比とスウィングの量に合わせて、ガイド音を鳴らす位置をどう求めるようにしたかを書きます。前に保存した設定の扱いと、増えたパターンから欄に出すものを選べるようにしたことにも触れます。
ガイド音は「拍の中の位置」で決まる
ガイド音の位置は、パターンごとの関数が「拍の頭から 1 拍のどれだけ先で鳴らすか」を 0 以上 1 未満の数で返し、シーケンサー(音を鳴らす時刻を決める部分)がそれに 1 拍の長さを掛けて時刻にします。拍の頭では拍のクリックが鳴るので、0 は入れません。8 分音符なら [1/2]、3 連符なら [1/3, 2/3] です。
5 連符・6 連符・7 連符・32 分音符も、1 拍を等しく分けた位置を返すだけです(32 分音符は 1 拍を 8 つに分けます)。
/** 1 拍を count 個に等しく分けたときの、拍の頭を除く位置 */
const evenlySpacedOffsets = (count: number) =>
Array.from({ length: count - 1 }, (_, index) => (index + 1) / count);
case "quintuplet":
return evenlySpacedOffsets(5);
case "32nd":
return evenlySpacedOffsets(8);この形のままでは済まなかったのは、1 拍の中に収まらないポリリズムと、位置そのものを動かすスウィングです。
ポリリズムは拍ごとに求める
ポリリズムの欄で、音の数と拍の数をそれぞれ 2〜9 から選べます。4 と 3 なら、3 拍のあいだに 4 回、等しい間隔でガイド音が鳴ります。

- ボタンの下に、選んでいる比が出る
- 音の数と拍の数
ポリリズムには、比が 1 周する拍のまとまりがあります(3 対 2 なら 2 拍。以下、周期と呼びます)。これまでの 3 対 2 は、周期の頭の拍で、2 拍ぶんの音の時刻をまとめて前もって決めていました(予約)。最後の音は、予約した拍の頭から 4/3 拍先です。同じやり方で比を選べるようにすると、7 対 4 では 3 拍以上先の音までまとめて予約することになります。
先の拍の音をまとめて予約すると、そのあいだにテンポが変わったときに位置がずれます。シーケンサーは、予約した時点の 1 拍の長さで時刻を決めるためです。テンポを少しずつ上げるプログレッシブモードでは、周期の途中でテンポが変わります。
そこで、拍ごとに「その拍に入る音」だけを求めるようにしました。音の数を notes、拍の数を beats とし、周期の頭の音を 0 番目と数えると、k 番目の音は周期の頭から k × beats / notes 拍の位置です。これを、周期の中で何拍目か(beatInCycle)と拍の中の位置に分けます。
const getPolyrhythmOffsets = (
[notes, beats]: Readonly<PolyrhythmRatio>,
totalBeatIndex: number,
) => {
const beatInCycle = ((totalBeatIndex % beats) + beats) % beats;
// 位置は k × beats / notes 拍。小数の誤差で拍の境目を取り違えないよう、整数のまま拍と余りに分ける
return Array.from({ length: notes - 1 }, (_, index) => (index + 1) * beats)
.filter((position) => Math.floor(position / notes) === beatInCycle)
.map((position) => (position % notes) / notes);
};位置を k × (beats / notes) のように小数で求めると、拍の境目にちょうど来る音(4 対 2 なら、周期の頭から 2 拍目の頭に来る音)が、計算の誤差で前の拍の終わりと取り違えられるおそれがあります。そのため、k × beats を整数のまま notes で割り、商を拍、余りを拍の中の位置にしています。
7 対 4 なら、4 拍の周期の中で次の位置に鳴ります。周期の頭の音は拍のクリックと重なるので、ガイド音としては鳴らしません。
周期の中の拍 | 鳴る位置(拍の中) |
|---|---|
1 拍目 | 4/7 |
2 拍目 | 1/7・5/7 |
3 拍目 | 2/7・6/7 |
4 拍目 | 3/7 |
周期は、小節の中の拍番号ではなく、再生を始めてからの通しの拍番号で数えます。3/4 で 3 対 2 を鳴らすと、2 小節・6 拍で 1 周し、小節をまたいでも等間隔に鳴ります。
メトロノームの音を音声ファイルに書き出す機能も、同じ関数で位置を決めています。そのため、3 対 2 の書き出しの波形はわずかに変わりました。2 拍目の音を「前の拍の頭から 4/3 拍」ではなく「その拍の頭から 1/3 拍」で求めるようになり、サンプル(音声のデータの 1 点)の位置の切り捨て方が変わったためです。ずれは最大 1 サンプルで、書き出しの 44.1kHz では約 0.02 ミリ秒です。
スウィングは 1 つの量にまとめた
これまでは、16 分音符を跳ねさせる「16th(s)」というパターンと、カスタムビート(パターンの Custom で、自分でリズムを組む機能)の「シャッフル」のスイッチがありました。どちらも、16 分音符の 2 つ目と 4 つ目を 3 連符のリズムの位置(8 分音符の長さの 2/3)へずらすだけで、跳ね方の強さは変えられませんでした。
16th(s) を別のパターンとして残したまま強さも選べるようにすると、「跳ねない 16 分」と「跳ねる 16 分」の違いが、ボタンとスライダーの 2 か所に分かれます。跳ねないのはスウィング 50% のときとすれば、選ぶ場所は 1 つで済みます。
そこで今回、16th(s) をなくして 16th に一本化し、8th と 16th を選んだときにスウィングのスライダーを出すようにしました。カスタムビートのシャッフルのスイッチも、同じスライダーに置き換えています。

- 16th を選ぶと、下にスライダーが出る
- スウィングの量(8th・16th とも 50〜75%)
スウィングの量は、跳ねさせる 2 つの音のうち、前の音が占める長さの割合です。8th なら 1 拍を、16th なら 8 分音符 1 つぶんを前後 2 つに分け、前の音の長さがこの割合になるところまで、後ろの音を遅らせます。
case "8th":
// 裏の 8 分をスウィングの量だけ遅らせる(0.5 なら拍の真ん中)
return [swingRatio];
case "16th":
// 8 分音符 2 つそれぞれの中で、後ろの 16 分音符をスウィングの量だけ遅らせる(0.5 なら等間隔)
return [swingRatio / 2, 1 / 2, 1 / 2 + swingRatio / 2];16th なら、50% で跳ねない 16 分音符、67%(2/3)でこれまでの 16th(s) と同じ 3 連符のリズムの位置、75% で付点 16 分音符の跳ね方になります。8th で跳ねさせると、ジャズの 8 分のスウィングも練習できます。
16th(s) を使った前の設定は、スウィング 67% で読み込む
保存した設定・ブラウザに残っている前回の状態・書き出した設定ファイルには、16thShuffle(画面の 16th(s))や、カスタムビートのシャッフルをオンにした状態が残っています。読み込むときは、どれも同じ関数で「16th(またはカスタムビート)で、スウィング 2/3」として扱うので、これまでと同じ鳴り方のまま使えます。
export const migrateLegacySwing = ({
guideNoteType,
swingRatio,
isCustomBeatShuffleEnabled = false,
}: {
guideNoteType: unknown;
swingRatio?: unknown;
isCustomBeatShuffleEnabled?: boolean;
}): { guideNoteType: GuideNoteType; swingRatio: number } => {
const swing = normalizeSwingRatio(swingRatio);
const legacySwing =
swing > DEFAULT_SWING_RATIO ? swing : LEGACY_SHUFFLE_SWING_RATIO;
if (guideNoteType === "16thShuffle") {
return { guideNoteType: "16th", swingRatio: legacySwing };
}
if (guideNoteType === "custom" && isCustomBeatShuffleEnabled) {
return { guideNoteType: "custom", swingRatio: legacySwing };
}
return {
guideNoteType: isGuideNoteType(guideNoteType) ? guideNoteType : "off",
swingRatio: swing,
};
};鳴り方が変わっていないかは、音声の書き出しの波形で確かめました。書き出しのテストでは、パターンごとに、書き出した波形から求めたハッシュ(値が完全に一致するかを見るため、波形のバイト列から求めた短い値)を記録しています。16th(s) とカスタムビートのシャッフルのテストを「16th でスウィング 2/3」「カスタムビートでスウィング 2/3」に書き換えても、記録したハッシュは変わりませんでした。前の節の 3 対 2 とは違い、こちらは波形が変わらなかったということです。
出すパターンは自分で選ぶ
パターンが 10 個になり、そのまま並べると 3 段になります。5 連符や 32 分音符を使わない人には、段が増えるだけです。
そこで、テンポや拍子の欄にあるプリセットの「編集」と同じように、パターンの欄にも「編集」を付けました。開いたモーダルで、パターンの欄に出すものを選べます。

- はじめはオフにしている 5 連符・6 連符・7 連符・32 分音符
はじめに出すのは、これまでもあったパターン(Off・8th・Triplet・16th・Poly・Custom)だけです。5 連符・6 連符・7 連符・32 分音符は、使う人が「編集」でオンにすると欄に出ます。どれを出すかは、設定を保存すると一緒に残ります。
マニュアルのパターンの節にも、使い方を書いています。