0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Musescoreで作成した譜面の移調を自動化 (Python, Music21, MusicXML)

Posted at

導入

概要

Musescoreで作成した譜面の移調を自動化するスクリプトをPythonで作成した。

背景

筆者は趣味でジャズギターを弾く。
練習のための譜面をMusescoreで作成することが多い。
移調はMusescore上でのマウス操作でも可能だが、12キーへの移調をすべて行うのは手間がかかるのでプログラミングで自動化することにした。

環境

Host OS: Windows 11
Guest OS: Ubuntu 22.04.4 LTS (WSL2)
Python version : 3.10.12
Music21 version: 9.1.0
Musescore (WSL): musescore3/jammy,now 3.2.3+dfsg2-11 amd64

詳細

使い方

スクリプト実行前のディレクトリは以下の通り。

.
├── README.md
├── change_key.py
├── jazzblues_combo.musicxml
├── read_mxl_split_write.py
├── requirements.txt
└── .venv

jazzblues_combo.musicxmlが今回の転調したい譜面ファイル。
.msczではなく、.musicxml形式のファイルを使います。

Host OS(Windows11)上Musescore4で開くと以下の通り。
image.png

python change_key.pyコマンドでスクリプト実行したあとのディレクトリは以下の通り。

.
├── README.md
├── change_key.py
├── jazzblues_combo.musicxml
├── jazzblues_combo_12keys
│   ├── jazzblues_combo_00_-6_B.musicxml
│   ├── jazzblues_combo_01_-5_C.musicxml
│   ├── jazzblues_combo_02_-4_C#.musicxml
│   ├── jazzblues_combo_03_-3_D.musicxml
│   ├── jazzblues_combo_04_-2_E-.musicxml
│   ├── jazzblues_combo_05_-1_E.musicxml
│   ├── jazzblues_combo_06_00_F.musicxml
│   ├── jazzblues_combo_07_01_F#.musicxml
│   ├── jazzblues_combo_08_02_G.musicxml
│   ├── jazzblues_combo_09_03_G#.musicxml
│   ├── jazzblues_combo_10_04_A.musicxml
│   └── jazzblues_combo_11_05_B-.musicxml
├── read_mxl_split_write.py
├── requirements.txt
└── .venv

新しいディレクトリと12個のファイルが作成される。
11個のファイルは移調されたもの、1つのファイルは移調前と同じもの。

移調されたファイルのひとつ、jazzblues_combo_11_05_B-.musicxmlをHost OS(Windows11)上Musescore4で開くと以下の通り。
image.png

初期設定

順番が前後しましたが、スクリプトを実行する前の初期設定についてです。

musescore

aptで探して手に入るものをインストール

# check available musescore  (Ubuntu)
apt list -a | grep musescore

# install musescore3 (Ubuntu)
sudo apt install musescore3

Python 設定

# Create venv for Python (Ubuntu)
python3 -m venv .venv

# activate venv
source .venv/bin/activate

# install music21
python -m pip install music21

# create python package file
python -m pip freeze > requirements.txt

Script

全体

change_key.pyが今回のメインのスクリプト。

import os
import music21
import logging

# setup log on console
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# variables
filename_body = "jazzblues_combo"
original_key_str = "F"
filename_suffix = ".musicxml"
startval = -6  # start key from the original key
steps = 12  # transporse range. If you want all key, set 12.

input_file = "./" + filename_body + filename_suffix
logger.debug(f"input_file: {input_file}")

output_dir = "./" + filename_body + "_12keys"
os.makedirs(output_dir, exist_ok=True)
logger.debug(f"output_dir: {output_dir}")


for idx_i, i in enumerate(range(startval, startval + steps)):
    score = music21.converter.parse(input_file)  # load original file.
    the_key = music21.key.Key(original_key_str)
    score.insert(0, the_key)
    score.transpose(i, inPlace=True)
    file_name = (
        filename_body
        + "_"
        + str(idx_i).zfill(2)
        + "_"
        + str(i).zfill(2)
        + "_"
        + score.keySignature.tonicPitchNameWithCase
        + filename_suffix
    )
    logger.debug(f"file_name: {file_name}")
    file_path = os.path.join(output_dir, file_name)
    score.write(fmt="musicxml", fp=file_path)

解説

変数

# variables
filename_body = "jazzblues_combo" # 移調したいファイルの名前から拡張子を除いたもの

original_key_str = "F" 
# 移調したいファイルのキー。
# 書いておかないと譜面のキーを判定してくれない。大文字がmajor key, 小文字が minor key。

filename_suffix = ".musicxml"
# 譜面ファイルの拡張子。移調前と移調後で共通

startval = -6  # start key from the original key 
# この場合、オリジナルキーの-6からスタートしてstepsの値まで処理を繰り返す。

steps = 12  # 全部のキーへ移調する場合は12に設定.

メイン処理

for idx_i, i in enumerate(range(startval, startval + steps)):
    score = music21.converter.parse(input_file)  # 譜面のmusicxmlファイルからスコアを作成
    the_key = music21.key.Key(original_key_str) # key情報を文字列からMusic21のオブジェクトへ変換
    score.insert(0, the_key) # key情報を付与
    score.transpose(i, inPlace=True) # 移調処理
    file_name = (
        filename_body
        + "_"
        + str(idx_i).zfill(2)
        + "_"
        + str(i).zfill(2)
        + "_"
        + score.keySignature.tonicPitchNameWithCase
        + filename_suffix
    ) # パッと見で何番目に作られたどのキーの譜面なのかわかるように出力ファイル名を付与
    logger.debug(f"file_name: {file_name}")
    file_path = os.path.join(output_dir, file_name)
    score.write(fmt="musicxml", fp=file_path) # ファイル出力

logger

本筋とは全然関係ないが、
printデバッグしてコメントアウトしたりするよりもlogerの方が、簡単で読みやすい。

初期設定。今回はファイル出力せずにCLIへ出力

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

開発中はこうするとlogger.debug("何かしらの文字列")の部分が出力される

logging.basicConfig(level=logging.DEBUG)

終わったらこうすると余計なログを一発で止めることができる。

logging.basicConfig(level=logging.INFO)

おまけ 小節毎に分割

read_mxl_split_write.pyというのも作った。
特定のPart(トラック)を小節毎に分割して、各小節を1ファイルとして保存する。
これだけだとあまり役に立たないが、練習がてら書いてみた。

import os
import music21
import logging

# setup log on console
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

# variables
filename_body = "jazzblues_combo"
original_key_str = "F"
filename_suffix = ".musicxml"
part_idx = 0

startval = -6  # start key from the original key
steps = 12  # transporse range. If you want all key, set 12.

input_file = "./" + filename_body + filename_suffix
logger.debug(f"input_file: {input_file}")
logger.debug(f" ")

output_dir = "./" + filename_body + "_separated_bars"
os.makedirs(output_dir, exist_ok=True)
logger.debug(f"output_dir: {output_dir}")


score = music21.converter.parse(input_file)


the_part_mesures = score.getElementsByClass(music21.stream.Part)[
    part_idx
].getElementsByClass(music21.stream.Measure)
print(the_part_mesures)
print(len(the_part_mesures))

for idx_i, obj_i in enumerate(the_part_mesures):
    file_name = (
        filename_body
        + "_"
        + str(idx_i + 1).zfill(2)
        + "_part"
        + str(part_idx + 1).zfill(2)
        + filename_suffix
    )
    logger.debug(f"file_name: {file_name}")
    file_path = os.path.join(output_dir, file_name)
    tmp_score = music21.stream.Score()
    tmp_score.append(obj_i)
    tmp_score.write(fmt="musicxml", fp=file_path)

まとめ

感想

オブジェクト構造をつかむまでが難しかった。
まず 大きい順にScore, Parts, Measure (小節?)
その配下に並列でNotes(音符), Rests (休符), Chord (コード)があるっぽい。

簡単に移調ができるようになってよかった!

今後

機械的に練習パターンを作れるようにしたい。例えば...

  • 気に入ったフレーズをコード進行的に合うように加工して貼り付ける
  • 特定のリズミックなモチーフをコードに合うようにして貼り付ける

異名同音(A flatになっていてほしいところがG sharpになっている)ところを調整する方法を探りたい。

musicxmlからうまいことキー情報を読み込めるようにして、キーを使用者がスクリプトに明示的に記載する必要がないようにしたい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?