BenzaitenAdlibのざっくり解説
これは何?
先日開催された「AIミュージックバトル!弁財天」にて使用したプログラム BenzaitenAdlib の後処理について説明する記事です。
当日の解法共有LTでもお話しましたが、このプログラムは学習フェーズが公式サンプルのものと同じであるため、この記事では後処理についてのみ説明します。
用語
- ノート とは、音符のことです。
-
ノート番号 とは、ノートの音の高さのことで、雑に言うと鍵盤でのキーの番号です[1]。
から0 までの値をとり、ト音記号表記時の下第1線が127 です。60 - 音のクラス とは、あるノート番号の十二平均律における音程で、要するにノート番号を
で割った余りです12 - オクターブ とは、一般に8度音程のことですが、この記事においては、音名の指定がない状態で登場した場合に「あるC(C, ハ音)からその上の最も近いB(H, ロ音)までの区間」を指します。
特殊記法
- 有限数列
の要素数はa_n で表します。\text{len}(a_n) - ノート番号
が属するオクターブにおけるクラスn の音をc で表します[2]。\mathbb{O}_n(c)
その他
- 数列のインデックスはすべて1始まりです。
動作概略
モデルが生成したメロディラインを受け取り、下記のA,B,C,Dを順番に適用します。
A. メインノート列補正
モデルが生成するメロディは、
これらに対してそれぞれ処理を行います。
ただし、
- 処理中のノート番号を
とします。n (0 \le n \le 127) -
が属する小節の伴奏に設定されたコードをn (斜体のC)とします。C_n -
の構成音クラスの列をC_n とします。\{c_n\} (0 \le c_n \le 11)
以下、 A0, A1, A2, A3 の順に実施されますが、A1 と A2 は、いずれか一つだけ実施されます。
A0: コードサフィックスの確率的除去
関数の引数として与えられる確率
自明ですが、サフィックスを除去すると
A1: 「攻め」の補正
A1-1: 開始音補正
最初から3個の16分音符(結合処理により実質符点4分音符1個分)についてはノート番号を
ただし
確率(%) | 値 |
---|---|
60 | |
40 |
|
A1-2: 第5小節補正
ただし、
-
Q=\{\mathbb{O}_{k+6}(c_{\text{len}(c_n) - 2}), \mathbb{O}_{k+6}(c_{\text{len}(c_n) - 1}), \mathbb{O}_{k+6}(c_{\text{len}(c_n) - 1}) + 2 \} - ただし
を第4小節の最後のノート番号とするk
- ただし
-
は、ノートj が第5小節内の16分音符n 個目であるとしてm をm + 1 で割った余り3
A2: 「守り」の補正
A2-1: 低すぎる音の修正
ノート番号が
選択肢 | 確率(%) | 値 |
---|---|---|
1 | 50 | |
2 | 50 |
|
A2-2: コード構成音を考慮した補正
ノートがコード
A2-3: 黒鍵補正
- プログラムの引数
strict_mode
がTrue
になっている。 - 以下の両方の条件を満たしている:
- コード
がサフィックスを持っているC_n - 過去に黒鍵補正を行った回数を
で割った余りが5 1
- コード
補正することが確定した場合、
確率(%) | 値 |
---|---|
50 | |
50 |
A2-4: 同音補正
ただし、以下で「これまでに同音補正を行った回数を
- 禁止クラスリスト
を作り、E の1個前のノートのクラスを挿入します。n -
が真、かつ、P の前に音が存在するなら、n にE の2個前のノートのクラス を追加します。n -
が真、かつ、P の1つ前および2つ前に音が存在する、かつ、コードn の構成音がC_n つ以上なら、4 にE の3個前のノートのクラス を追加します。n -
に含まれるノートクラスで、C_n に含まれないものをすべて選び、この集合をE とします。F -
からランダムに一つを選んだものをF とすると、a をn で置き換えます。\mathbb{O}_n(a)
A3: 音程補正
A3-1 と A3-2 は常に実施されます。
A3-1: 高すぎる音の補正
ノート番号80(G#5)よりも高い音は1オクターブ下げます。
A3-2: 低すぎる音の補正
ノート番号58(B♭3)よりも低い音は1オクターブ上げます。
B. 9小節目のノート補完
8小節目最後の音を
- 集合
の音を16音符の長さにして順番に並べます。\{ \mathbb{O}_k(x) | x \in c_k \} -
のルート音を「16分音符1個 + 2分音符1個」の長さにして追加します。C_n
C. 同一ノートの結合処理
同じ高さの16分音符を結合して8分音符や4分音符などにします。
こちらはスターターキットと全く同じ実装です。
D. ピッチベンドイベントの追加
生成されたMIDIに対して、以下の条件でピッチベンド(pitchwheel
)を挿入します[4]。
トラック1のノートそれぞれに対して、繰り返し以下の処理を行います:
以下の条件のいずれかを満たしている場合は、ピッチベンドイベントを追加します。
- 現在処理中のノートが最初の音である
- ノートの長さが8分音符以上、かつ、最後にピッチベンドイベントを挿入してから符点2分音符1個分の時間だけ経過している
上記条件のいずれかを満たしている場合、ノートの最初を16分音符1個分削り、ピッチベンドイベントを挿入します。
ただし、ビッチベンドカーブは以下の条件で決定します。(
|
カーブ | 概形 |
---|---|---|
8分音符以上 | 凹カーブ | |
約符点8分音符以上 | 凸カーブ |
ソースコード
-
https://newt.phys.unsw.edu.au/jw/notes.html などが参考になります。 ↩︎
-
引数を添え字で表現するのはよいプラクティスではないかもしれませんが、今回は見やすさのためにこのように定義しました。 ↩︎
-
もっと言えば、目的の音域に収まるようにwhile文で実装しなければならないところですが、できてませんね... ↩︎
-
ちなみに、このピッチベンドイベントですが、試合本番では流れていませんでした。再生環境とローカルの環境に差異があったのが原因のようですが、はっきりとはわかっていません。 ↩︎
Discussion