VS Code (+Git) でPMD作曲環境を構築


概要

  • Windows11環境で、昔のパソコンのFM音源の曲(のMML)を書けるようにしたい!
    • いわゆる「PMD86」の曲を書きたい(東方封魔録~怪綺談の曲みたいな)
    • FMPでもほぼ同じようなことが可能だと思う(PMDにこだわりがなければFMP7の方が良いと思う……)
  • Microsoft製テキストエディタ「Visual Studio Code」(以下VS Code と表記)を使用
  • VS Code から自動でコンパイルと再生が行えるようにする
  • 追記: ついでにGitでバージョン管理できるようにする
    • MMLはテキストで記述するため、恐らくそういうことができる

Gitを使ったテンプレートはこちら!

PMD-Template

PMDってなんぞや

PC-9800シリーズは、1980~90年代にかけて一世を風靡したNEC製のパソコンで、現在広く使われているパソコンとは少し系譜の異なるものです。

コレが流行っていた頃は今のようにコンピュータの能力が高くなかったので、OSは「DOS」というキーボードのコマンド入力だけで操作するもの(初期はそもそもOSなんてなかった)、ゲームなどで音を鳴らそうにも録音した音源なんてファイルサイズがデカすぎて不可能でした。そのため、「FM音源」と呼ばれる方式のシンセサイザー(用の音源チップ)をパソコン用に作り、それをプログラムから鳴らしていました。その音を鳴らすための拡張ボードが、いわゆる26音源とか86音源とかです。しかし鳴らすためには複雑なプログラミングスキルが求められて大変でした。そのため、簡単に鳴らすための「音源ドライバ」というものが開発されました。

「P.M.D.」とは、「Professional Music Driver」の略で、KAJA氏が作成したFM音源ドライバのことです。PC-9800シリーズだけでなく、PC-8801やFM Townsなどにも対応していました。私が生まれる前の話なので見たわけではないのですが、かなり使いやすい音源ドライバだったようで、様々なWebサイトでその歴史や好評価が語られています。「プリンセスメーカー」や「同級生」などが、PMDが使われたゲームとして有名だと思います。かの有名な東方projectも、最初期はPC-98シリーズ用のゲームとして制作され、ここでもPMDが使われていました。(既にPC98の衰退期だったようですが…。) 他にもFMPというドライバも有名です。Windowsで動くFMP7というソフトでは64パートまで同時に演奏できます。(86音源ではFMx6+SSGx3+PCM)

今でもFM音源、そしてPMD楽曲を好む人はたくさんいます。そんな方々の手によって、音源を現代のPCで再現するエミュレータや、新たなPMD楽曲が生み出されています。

お借りしたもの

  • P.M.D. (Professional Music Driver) (KAJA氏作)

    • かぢゃぽんのお部屋
    • 「PMD98」のVer.4.8sをダウンロード
    • 当然PC-9801用ソフトウェアだが、なんと2023年に最新版がリリースされている
    • 「PMD98用 Preset FM音色セット」もあると音色探しに便利
  • MS-DOS Player for Win32-x64 (TAKEDA氏作)

  • FMPMD2000 / PMDWin.dll (C60氏作)

    • C60のページ ->「自作ソフトのDownload」から、これら2つを落とす
    • PMDなどのFM音源用の曲データをWindows上で再生するためのソフト及びライブラリ。
  • YM2608用リズム音源6種

  • Visual Studio Code

  • Run on Save

    • VS Code 用拡張機能。ファイル保存時に、指定された動作が自動で行われる
    • 同じような拡張機能は複数存在するが、VS Code のターミナルを操作できる(すなわちShift-JIS環境でログを表示できる)拡張は、私の調べではコレしか無かった

おまけ(必須ではない)

  • bigyihsuan/pmd-mml-syntax-highlighting
    • VS Code 用拡張機能。PMD用のシンタックスハイライトをしてくれる。
      • ストアで配布していないので自分でビルドする必要あり
  • hoot sound hardware emulator
    • サウンド専用ハードウェアエミュレータ。PMDも再生できる。
    • 導入が少し難しいが、PMDWinより良い音が鳴る(気がする。少なくともリズム音源に関しては。)
      • なんとなくPMDWinより音のバランスも実機に近い気がする(私は実機を持っていないので、実機録音したであろう「幺樂団の歴史」との比較しかできないが)

ディレクトリ構成

  • \.vscode
    • VS Codeの設定とか(のうち、このワークスペースにのみ効かせたい設定)
  • \bin
    • コンパイルおよび再生に使うプログラム・バイナリ類を入れる
  • \out
    • コンパイルした曲データ \srcと同じディレクトリ構成(になるようにバッチを組んだ)
  • \src
    • MMLファイルを入れる
    • 音色セットのMMLファイルも入れておくと便利
  • compile.bat
    • 自動コンパイル用のバッチファイル。詳細は後述。

曲の作り方

拙作テンプレートを使う場合は、リポジトリのトップページの画面の右上にある "Use this template" ボタンをクリックして新しいリポジトリを作成してください。その後、作成したリポジトリをローカルにクローンしてください。クローンしたフォルダをVS Code で開くと、右下に「推奨される拡張機能が…」みたいな表示が出てくるので、指示に従ってRun on Saveをインストールしてください。

Gitじゃなくていい場合は、Run on Saveを導入して、以下を参考に作業環境を構築してください。

各種プログラムファイルをbinディレクトリにコピー

プログラムファイルをbinフォルダへコピーします。あるいは、バッチファイルの冒頭で各プログラムファイルの場所を指定します。

  • MC.EXE (PMD)
    • PMDのコンパイラ本体。
    • pmd48s.zipの直下にあるはず
    • PMDの記法がPMDMML.MANに記載されている
      • 他にも.DOCファイルに設定等書いてあるので読もう
  • msdos.exe (MS-DOS Player)
    • msdos.7zの中の\msdos\binariesに、想定するCPUの種類ごとにmsdos.exeがある
    • MC.EXEは16bitバイナリなのでどれでも動くはず
    • 筆者はなんとなく i486_x64 を選択 速そうだし
  • FMPMD.exePMDWin.dll
    • lzh形式は7-zip等の解凍ソフトで解凍する
    • PMDWin.dllは32bit版(\x86)を使う。
      • FMPMD.exeが32bitバイナリであるため
  • YM2608のリズム音源
    • これは別にどのフォルダに置いても良いんだけどね
    • 以下の6ファイルをコピーし、FMPMDの「環境設定」->「ディレクトリ」->「リズム音源waveディレクトリ」でリズム音源ファイルを入れたディレクトリを指定する。
    • 2608_bd.wav , 2608_hh , 2608_rim.wav , 2608_sd.wav , 2608_tom.wav , 2608_top.wav

MMLファイルを書いて、Ctrl + S

srcフォルダ内にMMLファイルを作成し、そこにMMLで曲を書きます。 srcフォルダの中にさらにフォルダを作成しても問題ありません。

ファイル名およびフォルダ名は8文字以内にするとよいでしょう。長いファイル名は、MS-DOS Player上では問題ありませんが、実機DOSでは当然扱えません。

書いたら、「Ctrl」+「S」キーを押して保存します。 すると、VS Codeのターミナル上でコンパイルが行われます。 正常にコンパイルされると、FMPMDが起動し、曲データが再生されます。

MMLの書き方について

MMLの文法は、PMDに付属している「PMDMML.MAN」を参照してください。
または"PMDMML.​MAN" HTML版

PMDの作者のKAJA氏が、YouTubeにてMMLでの曲の作り方を公開してくださっています。

MMLで作曲のススメ

あるいは、東方project楽曲の「旧作風アレンジ」を公開している方の中には、曲データだけでなくMMLファイルもいっしょに配布してくださっている方がそこそこいらっしゃるので、そういうのを参考にするのもよいかもしれません。私は大いに参考にさせていただいております。ありがとうございます!

拡張子の指定

MMLの冒頭に「全体制御コマンド」というものを書きます。このリポジトリを使う場合、以下の記述が必須です

#Filename .M

これは、出力されるファイルの拡張子を指定するコマンドです。PMDでは以下の拡張子を使います。(引用元: FMPMD2000のページ)

形式 説明
.M PMD(OPN)用の拡張子ですが、PMDB2, PMD86, PMDPPZの曲データでもこの拡張子のものもあるようです。
.M2 PMDB2 または PMD86 用の曲データです。
.MZ PMDPPZ 用の曲データです。

本来は拡張子だけでなくファイル名も指定できるのですが、このリポジトリで使うバッチファイルの仕様(というより制作者の好み)により、拡張子だけの指定となっています。

外部ファイルの指定(音色など)

音色ファイル等の外部ファイルはMMLファイル内で指定してください。リポジトリのルートからの相対パスで記述することができます。

例: 音色ファイル EFFEC.FF がsrcフォルダにあるとき

#FFFile     src\EFFEC.FF

作ってみた

MMLで「U.N.オーエンは彼女なのか?」のサビ
MMLで「U.N.オーエンは彼女なのか?」のサビ

シンタックスハイライト本当に助かる!私もMML初心者で稚拙だけど、書けると楽しいよ!

設定の内容

VS Codeの設定

  • VS Codeの主な設定は settings.json というファイルで管理されている
  • settings.jsonには「ユーザー設定」と「ワークスペース設定」の2種類がある
    • ユーザー設定の内容は、VS Code全体に適用される(現在PCで使用中のユーザーのみ)
    • ワークスペース設定の内容は、ワークスペース(VS Codeのウィンドウ)ごとに異なる内容を適用する
    • 設定の優先度はワークスペース>ユーザー の順
    • ワークスペース設定は、ワークスペース(VS Codeで開いているフォルダ)の .vsCode\settings.json
  • 以下の設定は「ワークスペース設定」に適用すべし
    • 「Ctrl」+「,」キーを押すと設定ウィンドウが出てくる
    • 出てきたウィンドウの左上の「ワークスペース」をクリック、その後、右上のマークをクリック
      • (紙に矢印が付いているマーク、カーソルを当てると「設定(JSON)を開く」と表示される)
    • 恐らく空のJSONファイルが作成されるので、以下のように書き換える
{
  "files.encoding": "shiftjis",
  "runOnSave.commands": [
    {
      "match": ".*\\.[mM][mM][lL]",
      "command": ".\\compile.bat ${fileRelative}",
      "runIn": "terminal"
    }
  ]
}

各項目の内容は以下の通りです。

  • "files.encoding": "shiftjis" デフォルトの文字コードを「Shift-JIS」に設定する。
    • 最近のテキストエディタ等では大体「UTF-8」を用いるが、MS-DOS PlayerやPMDのコンパイラでは当然使用できないため。
    • そもそもWindowsのコマンドラインはまだShift-JISだし
  • "runOnSave.commands": ファイル保存時に走らせるコマンド
    • "match": ".*\\.[mM][mM][lL]"
      • 対象にMMLファイルを指定(小文字も可)
    • "command": "bin\\compile.bat ${fileRelative}"
      • binディレクトリ内のcompile.batを起動する。引数は保存したファイルの相対パス。
    • "runIn": "terminal"
      • VS Code内のターミナルで実行するようにする

エラーコードによる処理分岐が必要なため、Run On Saveのコマンドでは処理をせず、バッチファイルにファイルパスを渡すようにしました。 compile.batについては後述。

ついでに、.vsCode\extensions.json も作っておきましょう。以下のように中身を指定すると、拡張機能を検索する手間を省けます。

{
  "recommendations": ["pucelle.run-on-save"]
}

コンパイル用バッチファイル

本来はコマンドプロンプト上で msdos.exe mc.exe [MMLファイル] を実行することでコンパイルを行います。Powershellだとファイル名がうまく出力されなかったりする。(VS Codeのターミナルも既定ではPowerShell。)

これを自動で行うために、compile.bat をターミナルから実行するようにします。compile.bat はワークスペース直下に配置しました。

処理の流れは以下のとおりです。

  • MMLファイルに記述した拡張子を抽出し、%OUTEXT% に格納
  • MMLファイルをコンパイルする
    • 曲データはMMLファイルと同じフォルダに生成される
  • エラーが出れば何もせず終了する。エラーが出なければコンパイラの処理が終了したとみなし、次の処理に移る。
  • コンパイルされた曲データは一旦MMLファイルと同じフォルダに出力される。
    • xcopyコマンドでsrcフォルダ内のフォルダ構造ごとコンパイル済み曲データを「out」フォルダにコピーし、コピー元を削除
  • プレイヤーを起動し、コピーした曲データを再生する。

PMDの曲データの拡張子には、.M.M2.MZ の3種類が存在します。また、MC.EXEが出力するファイルの名前と拡張子はMMLファイルの中の記述を優先的に参照します。故に、バッチファイルよって曲データのコピーや再生を行おうと思うと、MC.EXEとは別で出力ファイル名を取得する必要があります。今回は、MMLファイルの中では拡張子のみを指定するように制限を設け、その拡張子をバッチファイル側で判別することで出力ファイル名を取得できるようにしました。

現在私が使用している compile.bat のコードは以下のとおりです。

@echo off
chcp 932

rem ===========================
rem   パス指定部
rem ===========================
rem ※binフォルダ以外の場所のプログラムを使う場合はここで指定する

rem MS-DOS Player(msdos.exe)のパスを指定
set MSDOSEXE=bin\msdos.exe
rem PMDのコンパイラ(MC.EXE)のパスを指定
set MCEXE=bin\mc.exe
rem FMPMD2000(FMPMD.exe)のパスを指定
set FMPMDEXE=bin\FMPMD.exe
rem 出力先ディレクトリを指定(既定: out\)
set OUTDIR=out\

rem ============================
rem   ここまで
rem ============================

rem 入力ファイルのパス
set MMLPATH=%~1

rem ============================
rem   拡張子をMMLから抽出

rem 出力ファイルの拡張子
set OUTEXT=

findstr /R /I /B /C:"#Filename[ ]*.*\.[A-Za-z0-9][A-Za-z0-9]*" "%MMLPATH%" >nul
if %ERRORLEVEL% == 1 (
    echo #Filename の記述がないか、間違っています :%MMLPATH%
    goto :END
)else if %ERRORLEVEL% == 2 (
    echo findstrの文法エラー
    goto :END
)

findstr /R /I /B /C:"#Filename[ ]*\.[A-Za-z0-9][A-Za-z0-9]*" "%MMLPATH%" >nul
if %ERRORLEVEL% == 1 (
    echo #Filename には拡張子だけを記述してほしい :%MMLPATH%
    echo 例: #Filename .M
    goto :END
)

for /f "usebackq tokens=2 delims=." %%A in (`findstr /R /I /B /C:"#Filename[ ]*\.[A-Za-z0-9][A-Za-z0-9]*" "%MMLPATH%"`) do (
    set OUTEXT=%%A
    goto :EXT
)

:EXT
if not defined OUTEXT (
    echo 拡張子の抽出に失敗しました :%MMLPATH%
    goto :END
)

if %OUTEXT% == M goto :COMPILE
if %OUTEXT% == M2 goto :COMPILE
if %OUTEXT% == MZ goto :COMPILE

echo .M .M2 .MZ のいずれかの拡張子を指定してください
goto :END

:COMPILE
echo Extension = .%OUTEXT%

rem ============================
rem   コンパイル&再生

set OUTPATH=%OUTDIR%%MMLPATH:~4,-4%.%OUTEXT%

%MSDOSEXE% %MCEXE% %MMLPATH%

if ERRORLEVEL 1 (
echo compilation error.
) ELSE (
echo F | xcopy %~dpn1.%OUTEXT% %OUTPATH% /y /i
del %~dpn1.%OUTEXT%
start %FMPMDEXE% %OUTPATH%
echo Now playing %OUTPATH%
)

rem ============================

:END

以下、わかりにくい部分の解説

  • chcp 932
    • 使用する文字コードを明示的に指定するコマンド。多分要らないけど一応。
  • set MMLPATH=%~1
    • MMLファイルのパスを取得。保存したファイルのパスがそのまま入る。
  • set OUTPATH=%OUTDIR%%MMLPATH:~4,-4%.%OUTEXT%
    • 出力ファイル名とパスの指定。MMLPATHからフォルダ名「src\」と拡張子「.MML」を削除して使用している。
      • 文字数を指定して削除しているため、フォルダ名が変わったら「~4」の部分を書き換える必要がある
  • findstr とか、for 文の中身とか
    • 細かな説明をし始めると長くなってしまうので省略。
    • findstr は文字列検索コマンド。正規表現が使える。行を跨いだ文字列検索は行えない。
    • for文は、findstrコマンドの出力の各行を「.」で区切ったうちの、各行の2番目の文字列を変数%Aに代入する
      • findstrの結果は1行ずつ出力され、for文の中の処理はその1行ごとに行われる
      • #Filename の記述が複数あった場合、今回のバッチでは最初に書かれたものが採用される(setコマンドのあとすぐループを脱出している)
  • %MSDOSEXE% %MCEXE% %MMLPATH%
    • MS-DOS PlayerでMC.EXEを起動
      • MC.EXEの引数はMMLファイルのパス。

蛇足

まとめでもなんでもないんですけど、MMLファイルの名前を9文字以上にしても通常通りコンパイルが通ってしまった。MS-DOS Playerはともかく、DOS用のプログラム自体にはファイル名の制約はないのかもしれない。DOS上でファイルを扱うときは当然FATの制約を受けるため、特にこの発見に意味はないが…。

あと、「Visual Studio Code」の略称は「VSCode」ではなく「VS Code」らしいです。
そういうのにめっちゃ目ざとい先輩が言っていました。彼曰く、

ちゃんとスペース入れてる人は信頼できる人か AI 丸投げの二択です(両極端)

とのことなので、「私たちは機械じゃない! 人間なんだ……!」と思った方は「VSCode」表記をしてみると、人間味(にんげんあじ)を主張できるかもしれません。どっちでもよくないか…?

タグ: DTM , FM音源 , PMD , PC98 , VS Code

カテゴリ: 技術雑記