ノートをMMLに変換するスクリプト

注意点
  • ノートの最小分解度は 64分音符までです
  • 3連符、5連符、6連符といった連符は未実装です
  • 和音は考慮されません
参考

スクリプト

以下のスクリプトを拡張子「.pyscript」で保存して "piano roll scripts" フォルダに配置します。
from flpianoroll import *
 
# 音階定数.
NOTES = ["c", "c+", "d", "d+", "e", "f", "f+", "g", "g+", "a", "a+", "b"]
 
def number_to_mml(number, prev):
  """
  音階の文字列を返す
  オクターブ移動がある場合はそれも合わせて文字列連結する
  """
  ret = ""
  octave = int(number / 12) - 1 # FLは標準より1オクターブ高い.
  if prev is None:
    # initial.
    ret = "O%d  "%octave
  else:
    # 前回からの差分を求める
    octave_prev = int(prev / 12) - 1 # FLは標準より1オクターブ高い.
 
    d = octave - octave_prev
    if d != 0:
      # shift octave
      if d > 0:
        ret += "> " * d
      else:
        ret += "< " * abs(d)
 
  note = number % 12
  return ret + NOTES[note]
 
def ticks_to_mml(ticks, ppq=96):
    """
    MIDIティック数をMML音符長形式に変換する関数。
    :param ticks: MIDIティック数 (例: 96, 48, 72など)
    :param ppq: 分解能 (Pulse Per Quarter Note)。デフォルトは96。
    :return: MML形式の音符長 (例: "4", "8.", "4.^16")。
    """
 
 
    # 基本的な音符長のティック値
    note_lengths = {
        "1": ppq * 4,       # 全音符
        "2": ppq * 2,       # 二分音符
        "4": ppq,           # 四分音符
        "8": ppq // 2,      # 八分音符
        "16": ppq // 4,     # 十六分音符
        "32": ppq // 8,     # 三十二分音符
        "64": ppq // 16     # 六十四分音符
    }
 
    # 付点音符のティック値
    dotted_lengths = {
        "4.": ppq + ppq // 2,       # 付点四分音符
        "8.": (ppq // 2) + (ppq // 4),  # 付点八分音符
        "16.": (ppq // 4) + (ppq // 8), # 付点十六分音符
        "32.": (ppq // 8) + (ppq // 16) # 付点三十二分音符
    }
 
    # 複合音符のティック値(例: "4.^16")
    compound_lengths = {
        "4.^16": ppq + (ppq // 4),      # 付点四分音符 + 十六分音符
        "8.^32": (ppq // 2) + (ppq // 8) # 付点八分音符 + 三十二分音符
    }
 
    # 最も近い一致を探す
    closest_match = None
    closest_difference = float("inf")
 
    for mml, length in {**note_lengths, **dotted_lengths, **compound_lengths}.items():
        difference = abs(ticks - length)
        if difference < closest_difference:
            closest_match = mml
            closest_difference = difference
 
    # ティック数が六十四分音符より短い場合は None を返す
    if ticks < note_lengths["64"]:
        return None
 
    return closest_match
 
# -------------------------
# start program
# -------------------------
s = ""
note_prev = None
time_prev = 0
for i in range(score.noteCount):
  # ノート情報を取得 (和音は考慮しない).
  note = score.getNote(i)
  # 音階を決める
  pitch = number_to_mml(note.number, note_prev)
  time = note.time
 
  # 休符を入れる.
  d = time - time_prev
  if d > 0:
    len2 = ticks_to_mml(d)
    if not len2 is None:
      s += "r%s "%len2
 
  # next start time.  
  time_prev = time + note.length
 
  # 音階と長さを決める
  length = ticks_to_mml(note.length)
  if not length is None:
    s += "%s%s "%(pitch, length)
 
  note_prev = note.number
 
# ダイアログでテキストを出力する
Utils.ShowMessage(s)
 
 
最終更新:2025年03月27日 08:25