miso_soup3 Blog

主に ASP.NET 関連について書いています。

Jupyter Notebook と VS Code でバッハの平均律第1番を分析する

Jupyter Notebook で、楽譜を表示したり、その楽譜を再生してみたり、曲を分析したりしてみました。そのことについて書きます。分析といっても簡単なものです。

f:id:miso_soup3:20181220213826p:plain

目次

Code, .ipynb

環境

VS Code で Jupyter Notebook

VS CodeからJupyter Notebookを使ってみよう (1/3):Visual Studio Codeで始めるPythonプログラミング - @IT

こちらの記事を参考にしながらセットアップを行いました。VS Code拡張機能により、以下のように VS Code 内で結果が描画されます。

f:id:miso_soup3:20181216171853p:plain
VS Code で Jupyter Notebook

music21 とは

music21 は、音楽を解析するための Python ライブラリです。music21 の 21 とは、ここ によるとマサチューセッツ工科大学学部の 1 つである、Music and Theater Arts に付与された通し番号の 21 のようです。

どのような音楽データを解析できるかというと、MusicXMLMIDI、ABC記法のテキストデータなどの譜面データをサポートしています(ここの Converter.defaultSubconverters()から一覧を確認できます)。

music21 を使うと解析の他に、グラフ・楽譜・MIDI ファイルの出力ができるようで、それぞれ外部ライブラリを参照しています。グラフは Matplotlib ライブラリ、楽譜の画像の生成部分は LilyPond、楽譜データを表示するときは MuseScore などが参照されます(Extending music21 with Additional Software。自分の場合は、LilyPond.app と MuseScore.app を併用しました。これらの参照先は、次のようなユーザー設定を出力するコードで確認できます。

from music21 import environment
settings = environment.UserSettings()
for key in sorted(settings.keys()):
    print(key + ":")
    if key is not 'localCorpusPath':
        print(settings[key])

出力結果:

autoDownload:
ask
braillePath:
None
debug:
0
directoryScratch:
None
graphicsPath:
/Applications/Preview.app
ipythonShowFormat:
ipython.musicxml.png
lilypondBackend:
ps
lilypondFormat:
pdf
lilypondPath:
/Applications/Lilypond.app/Contents/Resources/bin/lilypond
lilypondVersion:
None
localCorporaSettings:
{}
localCorpusPath:
localCorpusSettings:
LocalCorpusSettings([])
manualCoreCorpusPath:
None
midiPath:
/Applications/Utilities/QuickTime Player 7.app
musescoreDirectPNGPath:
/Applications/MuseScore 2.app/Contents/MacOS/mscore
musicxmlPath:
/Applications/MuseScore 2.app/Contents/MacOS/mscore
pdfPath:
/Applications/Preview.app
showFormat:
musicxml
vectorPath:
/Applications/Preview.app
warnings:
1
writeFormat:
musicxml

曲の作成と表示

まずは、簡単に音を構築して五線譜で表示してみました。

from music21 import note,stream,corpus,chord,environment,converter,midi

stream1 = stream.Stream()
note = note.Note("C4", quarterLength = 1)
stream1.repeatAppend(note, 4)
stream1.show()

f:id:miso_soup3:20181220214110p:plain
ドを4つ組み立てて楽譜を表示

.show() ではなくstream1.show('xml') とすれば、MuseScore などの楽譜表示ソフトが立ち上がります。 耳で聞きたい場合は、stream1.show('midi') とします。VS Code拡張機能上では聞くことはできませんが、ブラウザ上の Jupyter Notebook では聞くことができます。

f:id:miso_soup3:20181216174744p:plain

コードで音を構築するのは面倒な場合、TinyNotation という記法が使えます。これは、music21 が LilyPond や ABC記法などを参考にして作った記法です。

例えば、TinyNotation でスーパーマリオのテーマの冒頭を記述するとこのようになります:

s = converter.parse('tinyNotation: 4/4 e8 e8 r e8 r c8 e8 r g4 r G4 r')
s.show()

f:id:miso_soup3:20181220214225p:plain

バッハの平均律を分析

実際に、バッハの平均律クラヴィア曲集 第1巻 第1番 プレリュード を分析してみました。ピアノの旧約聖書とも言われる平均律のうちの最初の曲、ハ長調で、多くの人が一度は耳にしていると思います。

www.youtube.com

この曲は楽譜でみるとこのように2ページになっています:

f:id:miso_soup3:20181216175811p:plain
music21に収納されているMusicXML形式のファイルをMuseScoreで開いたときの画面

全ての小節で似たような音形になっています。

music21 では、あらかじめいくつかの楽曲が MusicXML 形式で格納されており(場所)、次のようなコードで曲情報を扱うことができます。.measures(1, 2).show() で、1小節目と2小節目を表示しています。参照: User’s Guide, Chapter 11: Corpus Searching

from music21 import note,stream,corpus,chord,environment,converter,midi

#%%
bwv846 = corpus.parse('bach/bwv846')
bwv846.measures(1, 2).show()

f:id:miso_soup3:20181220214421p:plain

bwv846の意味は、BWVはバッハの作品番号で、846番がこの曲のことを指しています。

このように music21 の中にある MusicXML ファイルを参照できますが、ローカルにあるファイルを参照することも可能です。また、バッハの曲は433曲収納されていますが、そのうち平均律はこの1曲しかないようです。ちなみにショパンは1曲だけでした。

music21 は Matplotlib を参照してプロット図を生成します。

bwv846.plot()

f:id:miso_soup3:20181220214508p:plain

.plot() では、x軸が小節番号(時間軸)、y軸がピッチ(上が音が高く、下が音が低い)の図が出力されます。DTM でよくみるピアノロール式の表示です。この図からは、線の長さで上下で2つ別れているように見えます。これは上半分は右手のパートである、十六分音符が連続していて細かく動いていること、下半分は左手のパートであり、長い音が続いていることが分かります。これは、五線譜からみても分かることです。また、音の高さが時間が経つにつれ全体的に下降しているように見えます。

そして、他と比べて一定の高さの音が続いている箇所があります(下の図の赤線)。曲の最後の左手のソ(G)とハ(C)です。この曲はハ長調なので、属音から主音への解決を、割と長い時間をかけて行なっていることがわかります。

f:id:miso_soup3:20181220214809j:plain

また、途中で、左手が綺麗に音が降りているところがあります(上の図の青線)。ここを楽譜でピックアップして見てみると、確かに左手の2番目に弾く音が一音ずつ下がっていることが確認できます。

bwv846.measures(11, 16).show()

f:id:miso_soup3:20181220215207j:plain
11小節目から16小節目を表示

これらは、情報処理をせずとも楽譜からやMIDIDTMで表示したときに分かることで、ほとんどの演奏者も譜読みの段階で自ずと意識しています。気になった部分の小節を五線譜として表示したり、MIDIで耳で聞いてみたり、あるいは何か値を算出したりできるのが music21 ならではだと感じます。

分析 後半

次は、ヒストグラムを表示してみます。グラフ中の文字が小さくなるので、次のようにグラフの解像度を上げておきます。

import matplotlib as pyplot
pyplot.rcParams['figure.figsize'] = (20.0, 10.0)
pyplot.rcParams["figure.dpi"] = 200

ヒストグラムで音の高さ(ピッチ)毎に出現回数を表示してみます。

bwv846.plot('histogram', 'pitch')

f:id:miso_soup3:20181220215026p:plain

真ん中のド(C)を中心に左右に広がっています。ハ長調だからとはいえこんなに真ん中のド(C)が多くなるのでしょうか?他の曲と比べてみないとなんとも言えません。また、曲中で一番高い音はラ(A)、低い音はド(C)であることが分かります。

次の図は同じくピッチのヒストグラムですが、pitchClassと指定していることで、オクターブでグルーピングして出現回数を表しています。ハ長調なので、やはり白鍵(♯♭がついていない音)が多いです。

bwv846.plot('histogram', 'pitchClass')

f:id:miso_soup3:20181220225717p:plain

3Dの図も見てみたり:

bwv846.plot('3dbars', 'pitchClass')

f:id:miso_soup3:20181220215849p:plain

音の長さのヒストグラムも表示してみます。音の長さはquarterLenghと指定します。右手と左手で分けて表示してみます。四分音符は1で表され、八分音符は0.5、十六分音符は0.25です。

# 右手
bwv846.parts[0].plot('histogram', 'quarterLength', title='right hand')
# 左手
bwv846.parts[1].plot('histogram', 'quarterLength', title='left hand')
左手 右手
f:id:miso_soup3:20181220215444p:plain
左手の音の長さ毎の出現回数
f:id:miso_soup3:20181220215357p:plain
右手の音の長さ毎の出現回数

ここで、上の図から右手の0.25(十六分音符)の数が400ピッタリなのでは、と推測できます。図の解像度からは確信できないので、コードで右手の十六分音符を数えてみます。

f = bwv846.parts[0].flat
right16notes = [note for note in f.notes if note.quarterLength == 0.25]
len(right16notes)

出力:

400

本当に400でした。キリのいい数字です。

といったように、music21、Jupyter Notebook、VS Code を使えば曲の分析を行うことができます。今回は1曲だけでしたが、他にも、バッハの平均律全曲や、ショパンの練習曲全曲など、複数の曲を対象としてみたく思います。十二音技法も良さそうです。

参考

TECH PLAY女子部 Advent Calendar 2018 - Adventar

追記

ちょっと環境変えた後に描画できなくなっていたので調べたら、MuseScore 2 ではなく MuseScore 3 が入っていたためでした。以下のように設定を変えて描画できました。

settings = environment.UserSettings()
settings['musescoreDirectPNGPath'] = '/Applications/MuseScore 3.app/Contents/MacOS/mscore'
settings['musicxmlPath'] = '/Applications/MuseScore 3.app/Contents/MacOS/mscore'