PMX/MusiXTeXで日本語を使えるようにした

PMXとは、TeXで楽譜を作るパッケージであるMusiXTeXを簡単に使えるようにするツールである。筆者はたまにちょっとした楽譜を書き留めたい時とかにPMXを使うのだが、楽譜のタイトルや作曲者の所に日本語を使えないので、いつも諦めてローマ字で書いていた。

しかし、最近、タイトルを日本語で書いた楽譜を書いて印刷したいことがあった。
MacのGarageBandではタイトルが日本語の楽譜を印刷できることがわかったので、最初はこれで作ろうとしたが、GarageBandで楽譜を入力するのは私にはかなり難しかった。メトロノームの音に合わせてタイミング良くソフトウェアキーボード入力(またはMIDI端子で接続した電子ピアノから入力)した後で微調整するくらいで済むならできそうだが、私にはタイミング良くキーボード入力(鍵盤入力)するのができない。
Webで探すと、pTeXを使えばMusiXTeXで日本語を出せるという情報が多数あり、pTeXはインストール済みだったので、PMXで日本語を使う方法を探すことにした。

改めて、日本語のタイトルと作曲者名を含む、

2 1 3 4 3 4 1 0
0 8 20 0.0

bt
./
Tt
For Elise(エリーゼのために)
%"Für Elise"とするとドイツ語フォント問題が加わる
Tc
Ludwig van Beethoven(ベートーベン)
Ap
r4 rp ( a82 e+ a ) r r4 ( e82 e+ gs ) r r4 ( a82 e+ a ) r r4 /
( e85 ds e ds e b dn c a4 ) r8 ( c- e a b4 ) r8 ( e- gs b c4 ) r2 /

このようなソースコード(test.pmx)を作り、

pmxab test
musixtex test

としてできたPDFを見ると、やはり日本語フォントが出ず、ひらがなとカタカナ部分が消失した。

使用した環境は、Ubuntu系のOSで、apt-getでtexlive-musicとtexlive-lang-japaneseをインストールしたもので、PMXとMusiXTeXのバージョンはそれぞれ2.7.4と0.16、platexやeuptexでは普通に日本語が出る。

Webの情報を頼りに、

pmxab test
euptex test
musixflx test
euptex test
dvipdf test

としてみる(musixtexコマンドはetex, musixfix, etex, dvips, ps2pdfを順に実行するのと等価らしいので、実質etexをeuptexに変えただけ)と、日本語が出たが、日本語のフォントが小さくなった。

これはhttp://kuuku.no.coocan.jp/pub/musixtex/font/index.htmlに答えが書かれており、PMXが悪さをしているらしい。
同ページにて過去のpTeX用の、この問題を解決するマクロ(musixjfn-20080801.tar.gz内のmusixjfn.tex)が提供されているので、これを拾って、test.texの"\input musixtex"の次に"\input musixjfn"を追加してみたら、dvipdfで"dvips: ! invalid char 65288 from font jis"というエラーになった(65288=FF08hは「(」のUnicode)。

upTeXの既定の横書和文フォントのTFM名は
明朝: upjisr-h
ゴシック: upjisg-h
らしいので、musixjfn.texの中身を
jis → upjisr-h
jisg → upjisg-h
と置換してやり直すと、日本語フォントが大きくなった。

これで成功だが、pmxabが出力した.texファイルを編集しないといけないのでは、pmxabを実行する度に.texファイルの編集が必要になり、面倒である。PMXのドキュメント(pmx274.pdf)の"Putting TeX Commands into the PMX File"の所に、\\で始めると.texの上の方に埋め込まれると書かれているので、PMXのソースコードを次のようにしてみると、出力された.tex内の"\input musixjfn"の場所はだいぶ下になったが、上の画像と同じ、望み通りの結果が得られた。

2 1 3 4 3 4 1 0
0 8 20 0.0

bt
./

%日本語フォントのサイズを修正する
\\input musixjfn\

Tt
For Elise(エリーゼのために)
Tc
Ludwig van Beethoven(ベートーベン)
Ap
r4 rp ( a82 e+ a ) r r4 ( e82 e+ gs ) r r4 ( a82 e+ a ) r r4 /
( e85 ds e ds e b dn c a4 ) r8 ( c- e a b4 ) r8 ( e- gs b c4 ) r2 /

Roland HP-145の鍵を修理した

先週、我が家の電子ピアノ、Roland HP-145の3つの鍵(C5, C♯5, D5)が下がりっ放しで戻らなくなった。鍵を押すとクッションに当たるのでなく、ガチ、ガチと硬い物に当たる音がして、スピーカーからは通常より小さな音しか出なくなった。

現象が発生した瞬間は見ていないので、直接の原因はわからない。
この電子ピアノは買って21年が経過したが、のべ1,000時間くらいしか使っていない。これの10倍使われた同型機は沢山あると思う。それに、筆者は打鍵圧がかなり弱い(最初にキーがめちゃくちゃ軽いキーボードを使って練習したので、アップライトピアノを弾くとほとんど音が出ないくらいである)。購入後に7回の引っ越しを経たことはあるが、この使い方で何かが折れたり割れたりするとは思えなかった。それに、同時に3つの鍵がおかしくなったので、異物が挟まったのだろうかと思った。

もう古いので、新しい物に買い換えようかとも思ったが、何よりも、まず捨てるのが面倒で億劫である。しかし、そのままだと鍵の位置的に使い物にならない。古すぎて、お金を払って修理してもらう気にはなれない。

Rolandの電子ピアノの鍵を自力で修理したという話は、探せばいくつも見つかったし、大抵の人は簡単に分解したようだったので、やってみることにした。
分解のやり方がなかなかわからず、思いの外時間がかかったが、結果として、ドライバー1本で、5分もあれば分解できることがわかった。
またやるかも知れないので、鍵を修理する為の分解方法をここに控えておく。
  1. 天板を開ける
    天面の2つのネジと背面の上部の4つのネジを外して、天板を手前に少しずらすと、外れる。

  2. スライド蓋を外す
    スライド蓋のレールの途中にある、歯車の出入り口(【写真1】、左右両方)と、天面を支える金具(【写真2】、片方でOK)を外すと、スライド蓋を外すことができる。
    【写真1】
    【写真2】

  3. 鍵の上のクッション付きの横棒を外す
    左右それぞれ、1つは上から、1つは下からの2つのネジ(【写真3】)を外す。
    【写真3】

  4. 問題の鍵を外す
    鍵の上部のU字部分(【写真4】)を少し広げると外せる。(ただ、折れそうで、かなり恐い。)
    外すと、ハンマーのような部品が見える。
    【写真4】

鍵を外すと、C♯5の黒鍵のハンマー部品が折れており(【写真5】)、先端の部分が3つの鍵の下に跨るように落ちていた。
【写真5】
折れたハンマー部品を取り除き、多くの人がやっているように、最も使わなさそうな左端の黒鍵(A♯0)のハンマー部品を移植すると、直った。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

N = 10
np.random.seed(8)
df = pd.DataFrame({
    'A': np. random.randint(10, size=N),
    'B': np. random.randint(10, size=N),
    'C': np. random.randint(10, size=N)
}, index=pd.date_range('2020-09-01', periods=N))
df
A B C
2020-09-01 3 1 5
2020-09-02 4 3 5
2020-09-03 1 9 7
2020-09-04 9 2 9
2020-09-05 5 2 2
2020-09-06 8 6 6
2020-09-07 3 8 9
2020-09-08 8 9 5
2020-09-09 0 3 1
2020-09-10 5 4 6
このような時系列データがあるとし、A列を折れ線グラフ、B,C列を別のグラフに棒グラフで描画したものを、時間軸を合わせて縦に並べたいとする。
そこで、次のようなコードを実行すると、2段のグラフの内、上のグラフが描画されなかった。
fig, ax = plt.subplots(2, 1, sharex=True)
df['A'].plot(ax=ax[0], grid=True)
df[['B', 'C']].plot(kind='bar', ax=ax[1], grid=True)
plt.show()

下のグラフを描画しなければ、上のグラフが描画される。

fig, ax = plt.subplots(2, 1, sharex=True)
df['A'].plot(ax=ax[0], grid=True)
plt.show()

原因は、次のコードを実行するとわかった。

fig, ax = plt.subplots(2, 1, sharex=True)
df['A'].plot(ax=ax[0], grid=True)
print("xlim after 1st plot:", ax[0].get_xlim())
df[['B', 'C']].plot(kind='bar', ax=ax[1], grid=True)
print("xlim after 2nd plot:", ax[0].get_xlim())
実行結果
xlim after 1st plot: (18506.0, 18515.0)
xlim after 2nd plot: (-0.5, 9.5)
つまり、 pandas.DataFrame.plot は kind='line' と kind='bar' とで描画した後のX座標の範囲(xlim)が全く異なり、 matplotlib.pyplot.subplots(sharex=True) した状態でこの2つを描画すると、先に描画した座標系(Axes)のxlimが書き換えられてしまうのが原因である。
色々調べまくったが、棒グラフ表示にこだわると、pandas.DataFrame.plotを使って解決する方法は見つからなかった。(kind='scatter'の点グラフなら同じ問題が起こらないことを確認した。コードは省略)

Seabornを使っても、結果は同じだった。

import seaborn as sns
fig, ax = plt.subplots(2, 1, sharex=True)
df['A'].plot(ax=ax[0], grid=True)
print("xlim after 1st plot:", ax[0].get_xlim())
_ = df[['B', 'C']].melt(ignore_index=False).reset_index()
sns.barplot(x='index', y='value', hue='variable', data=_)
print("xlim after 2nd plot:", ax[0].get_xlim())
plt.show()
実行結果
xlim after 1st plot: (18506.0, 18515.0)
xlim after 2nd plot: (-0.5, 9.5)

結局、棒グラフだけ直接matplotlib APIを使って描画すると解決した。

fig, ax = plt.subplots(2, 1, sharex=True)
df['A'].plot(ax=ax[0], grid=True)

width = pd.Timedelta('0.4d')
ax[1].bar(df.index - width/2, df['B'], width=width, label='B')
ax[1].bar(df.index + width/2, df['C'], width=width, label='C')
ax[1].set_xlim(df.index[0] - width*2, df.index[-1] + width*2)
ax[1].grid(True)
ax[1].legend()

fig.autofmt_xdate()
plt.show()