投稿日:2026/3/4
Gemini に聞きながら(というかほぼ丸投げで)MusicXML を用いて web ページ上に楽譜を出力出来るようになったので,少しく書き留めておく.
Open Sheet Music Display というライブラリを用いて MusicXML を出力することが出来る.npm install opensheetmusicdisplay によって osmd をインストールし,次のようなコンポーネントを追加する(内容に関しては私はあまり理解していない).
MusicXmlScore.astro---
// src/components/MusicXmlScore.astro
const { scoreUrl = "/scores/sample.musicxml", zoom = 0.8} = Astro.props;
---
{/* カスタムタグ(<music-score>)でラップし、必要なデータを data-* 属性で渡します */}
<music-score data-score={scoreUrl} data-zoom={zoom}>
<div id={scoreUrl} class="score-container" style="width: 100%; height: auto;"></div>
</music-score>
<script>
// Viteのバンドル対象になるので、npmパッケージのインポートが正常に機能します
import { OpenSheetMusicDisplay } from "opensheetmusicdisplay";
class MusicScore extends HTMLElement {
async connectedCallback() {
// this.querySelector でこのコンポーネント内の div だけを取得
const container = this.querySelector(".score-container");
if (!container) return;
// data-* 属性から値を取得
const scoreUrl = this.dataset.score;
const zoomLevel = parseFloat(this.dataset.zoom || "1.0");
const osmd = new OpenSheetMusicDisplay(container);
osmd.setOptions({
drawTitle: false,
drawPartNames: false,
defaultColorMusic: "#FFFFFF",
defaultColorLabel: "#FFFFFF",
newSystemFromXML: true,
drawingParameters: "compacttight",
});
await osmd.load(scoreUrl);
osmd.zoom = zoomLevel;
osmd.render();
}
}
// カスタム要素としてブラウザに登録
customElements.define("music-score", MusicScore);
</script>あとは public/scores/ に .musicxml ファイルを置き,楽譜を表示したいページで import して(私の場合 import MusicXmlScore from '../../components/MusicXmlScore.astro'; )楽譜を置きたい場所に
<MusicXmlScore scoreUrl="/scores/20260304_2.musicxml"/>
と書けば良い.
.musicxml ファイルは直接書くこともできるが,MuseScore などのソフトウェアで作った楽譜を .musicxml で出力した方が簡便である(それでも面倒ではあるが).
例えば
example.musicxml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="3.1">
<identification>
<encoding>
<software>MuseScore 3.6.2</software>
<encoding-date>2026-03-05</encoding-date>
<supports element="accidental" type="yes"/>
<supports element="beam" type="yes"/>
<supports element="print" attribute="new-page" type="no"/>
<supports element="print" attribute="new-system" type="no"/>
<supports element="stem" type="yes"/>
</encoding>
</identification>
<part-list>
<score-part id="P1"></score-part>
</part-list>
<part id="P1">
<measure number="1">
<attributes>
<divisions>2</divisions>
<key>
<fifths>-3</fifths>
</key>
<time symbol="cut">
<beats>2</beats>
<beat-type>2</beat-type>
</time>
<clef>
<sign>G</sign>
<line>2</line>
</clef>
</attributes>
<note>
<pitch>
<step>C</step>
<octave>5</octave>
</pitch>
<duration>4</duration>
<type>half</type>
<stem>down</stem>
</note>
<note>
<pitch>
<step>E</step>
<alter>-1</alter>
<octave>5</octave>
</pitch>
<duration>4</duration>
<type>half</type>
<stem>down</stem>
</note>
</measure>
<measure number="2">
<note>
<pitch>
<step>G</step>
<octave>5</octave>
</pitch>
<duration>4</duration>
<type>half</type>
<stem>down</stem>
</note>
<note>
<pitch>
<step>A</step>
<alter>-1</alter>
<octave>5</octave>
</pitch>
<duration>4</duration>
<type>half</type>
<stem>down</stem>
</note>
</measure>
<measure number="3">
<note>
<pitch>
<step>B</step>
<octave>4</octave>
</pitch>
<duration>4</duration>
<type>half</type>
<accidental>natural</accidental>
<stem>down</stem>
</note>
<barline location="right">
<bar-style>none</bar-style>
</barline>
</measure>
</part>
</score-partwise>というコードによって
が得られる.