<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>ScoreStarling 部落格</title>
    <link>https://scorestarling.com/zh-hant/blog</link>
    <description>關於把錄音變成可編輯樂譜的研究、指南和故事。</description>
    <language>zh-tw</language>
    <lastBuildDate>Thu, 08 Oct 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://scorestarling.com/zh-hant/blog/feed.xml" rel="self" type="application/rss+xml"/>
    <image>
      <url>https://scorestarling.com/assets/starling-v2.png</url>
      <title>ScoreStarling 部落格</title>
      <link>https://scorestarling.com/zh-hant/blog</link>
    </image>
    <item>
      <title>吉他六線譜怎麼決定按哪條弦、第幾格</title>
      <link>https://scorestarling.com/zh-hant/blog/guitar-tab-strings-and-frets</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/guitar-tab-strings-and-frets</guid>
      <pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>指南</category>
      <description>同一個音在吉他指板上最多有六個位置，MuseScore 等打譜軟體會選能彈出它的音最高的那條弦，也就是最低的格。本文也說明這樣排什麼時候不順手，以及從 MusicXML 產生的六線譜為什麼會高一個八度。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>同一個音高通常能在二到六條弦上彈出來，所以六線譜必須選定一條。大多數打譜軟體（包括 MuseScore）會把每個音放在能彈出它的音最高的那條弦上，也就是最低的格；和弦裡某個音的弦如果已被佔用，就移到下一條空著的弦。這樣排出的指法把位低、好讀，但也可能讓旋律在一條弦上一路往上爬，而不是停在同一個把位。如果從檔案產生的六線譜整體高了一個八度，那是因為檔案把吉他記得比實際音高高一個八度，而軟體依照記譜音高排了格數。</p>
</section>

<h2 id="why-several-places">為什麼同一個音在指板上有好幾個位置？</h2>
<p>因為各弦的音域互相重疊。標準調音下，六條空弦依序是 E2、A2、D3、G3、B3 和 E4，相鄰兩弦相差五個半音，只有 G 弦到 B 弦相差四個，所以一條弦的第 5 格（G 弦是第 4 格）就是下一條弦的空弦音。中央 C 上方的 E，也就是 E4，可以是 1 弦空弦、B 弦第 5 格、G 弦第 9 格、D 弦第 14 格、A 弦第 19 格，或是低音 E 弦第 24 格：24 格的琴頸上有六個位置，22 格的電吉他或 19 格的古典吉他上則有五個。</p>
<figure class="diagram">
<svg viewBox="0 0 320 150" aria-hidden="true" focusable="false">
<text class="d-small" x="10" y="34">e</text>
<text class="d-small" x="10" y="49">B</text>
<text class="d-small" x="10" y="64">G</text>
<text class="d-small" x="10" y="79">D</text>
<text class="d-small" x="10" y="94">A</text>
<text class="d-small" x="10" y="109">E</text>
<line class="d-line" x1="30" y1="30" x2="310" y2="30"/>
<line class="d-line" x1="30" y1="45" x2="310" y2="45"/>
<line class="d-line" x1="30" y1="60" x2="310" y2="60"/>
<line class="d-line" x1="30" y1="75" x2="310" y2="75"/>
<line class="d-line" x1="30" y1="90" x2="310" y2="90"/>
<line class="d-line" x1="30" y1="105" x2="310" y2="105"/>
<line class="d-frame" x1="30" y1="26" x2="30" y2="109"/>
<line class="d-line d-dash" x1="88.3" y1="26" x2="88.3" y2="109"/>
<line class="d-line d-dash" x1="170" y1="26" x2="170" y2="109"/>
<line class="d-line d-dash" x1="310" y1="26" x2="310" y2="109"/>
<circle class="d-mark" cx="24" cy="30" r="5"/>
<circle class="d-mark" cx="88.3" cy="45" r="5"/>
<circle class="d-mark" cx="135" cy="60" r="5"/>
<circle class="d-mark" cx="193.3" cy="75" r="5"/>
<circle class="d-mark" cx="251.7" cy="90" r="5"/>
<circle class="d-mark" cx="310" cy="105" r="5"/>
<text class="d-small" x="30" y="128" text-anchor="middle">上弦枕</text>
<text class="d-small" x="88.3" y="128" text-anchor="middle">5</text>
<text class="d-small" x="170" y="128" text-anchor="middle">12</text>
<text class="d-small" x="310" y="128" text-anchor="middle">24</text>
<text class="d-small" x="160" y="146" text-anchor="middle">琴衍</text>
</svg>
<figcaption>標準調音下 E4 的六個位置：1 弦空弦，以及往下各弦的第 5、9、14、19 和 24 格。</figcaption>
</figure>
<p>五線譜只給出音高，六線譜給的是弦和格。所以不管是人還是程式來寫六線譜，都要替每個音從這些位置裡挑一個，而這個選擇決定了手放在哪裡。</p>

<h2 id="what-rule-apps-use">打譜軟體照什麼規則選弦？</h2>
<p>MuseScore 的規則寫在原始碼的 <code>StringData</code> 裡。一個音會被放到空弦音不高於它的弦當中，音最高的那一條（<code>convertPitch</code> 上方的註解寫著「using the highest possible string」，也就是盡量用最高的弦）。和弦則從最高的音往下逐一安排；如果某個音要用的弦已被佔用，軟體會「從最上面開始找一條合適的弦」（a suitable string, from topmost），也就是由上往下找第一條空著、而且能在指板格數範圍內彈出這個音的弦。如果一條也找不到，兩個音就留在同一條弦上，六線譜會標出這個衝突。</p>
<p>我們為 ScoreStarling 寫了同樣的規則，並比較兩者。一支腳本在標準調音、還沒填格數的六線譜譜表上隨機寫入 120 個和弦，每個和弦含一到四個不同的音，音域在 E2 到 C6 之間；再讓 MuseScore 4.7.5 的命令列把格數填上，然後逐音比對。我們換不同的隨機和弦，一共跑了四次。</p>
<div class="table-scroll" role="region" aria-label="480 個隨機和弦上，ScoreStarling 預設的弦和格與 MuseScore 的比較" tabindex="0">
<table>
<caption>480 個隨機和弦：MuseScore 4.7.5 與 ScoreStarling 預設規則比較，2026年10月8日</caption>
<thead><tr><th scope="col">和弦</th><th scope="col" class="num">數量</th><th scope="col">結果</th></tr></thead>
<tbody>
<tr><th scope="row">MuseScore 能排出來的</th><td class="num">447</td><td>全部 1,055 個音的弦和格都相同</td></tr>
<tr><th scope="row">MuseScore 把兩個音留在同一條弦上的</th><td class="num">13</td><td>高把位和弦；它匯入的吉他格數比我們的 24 格少，而我們把每個音都放在各自的弦上</td></tr>
<tr><th scope="row">怎麼排弦都彈不了的</th><td class="num">20</td><td>低音比能彈到它們的弦還多，例如兩個低於 A2 的音，只有低音 E 弦能彈；照 MuseScore 的寫法寫出並加上標記</td></tr>
</tbody>
</table>
</div>
<p>這條規則可以預測，所以很適合當預設值：看六線譜的人都能推算出每個音會落在哪裡。用 Guitar Pro 或 MuseScore 開啟這份樂譜，看到的格數也一樣。</p>

<h2 id="when-lowest-reads-badly">「低格優先」什麼時候會排得彆扭？</h2>
<p>當旋律落在指板高處時。這條規則從不看前後的音，所以凡是 1 弦能彈的音都會放到 1 弦上，吉他手原本會在同一個位置彈完的樂句，就變成在這一條弦上來回跑。下面是一個從第 12 格的 E 往下走的樂句，由 ScoreStarling 分別按兩種規則寫出。</p>
<figure class="tab-compare">
  <div class="tab-compare-row"><p class="tab-compare-name">低格優先</p><div class="notation-sheet"><!-- engrave:phrase-lowest --><svg class="engraved" viewBox="0 0 1100 313" role="img" aria-label="這個樂句依低格優先寫成的六線譜：所有音都在 1 弦上，從第 12 格下行到第 3 格" focusable="false"><svg class="definition-scale" viewBox="0 0 11000 3130"><g class="page-margin" transform="translate(0, 80)"><g class="system"><g class="measure"><g class="staff"><path d="M0 942 L1677 942" stroke-width="23"/><path d="M1929 942 L2647 942" stroke-width="23"/><path d="M3061 942 L3698 942" stroke-width="23"/><path d="M4112 942 L4749 942" stroke-width="23"/><path d="M5163 942 L6020 942" stroke-width="23"/><path d="M0 1257 L6020 1257" stroke-width="23"/><path d="M0 1572 L6020 1572" stroke-width="23"/><path d="M0 1887 L6020 1887" stroke-width="23"/><path d="M0 2202 L6020 2202" stroke-width="23"/><path d="M0 2517 L6020 2517" stroke-width="23"/><g class="clef"><use href="#ss-E06D" transform="translate(159, 1727) scale(0.72, 0.72)"/></g><g class="meterSig"><use href="#ss-E084" transform="translate(752, 1547) scale(0.72, 0.72)"/><use href="#ss-E084" transform="translate(752, 1907) scale(0.72, 0.72)"/></g><g class="layer"><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M1803 785 L1803 157" stroke-width="31"/></g></g><g data-id="p1" class="note"><text x="1803" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">8</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M2854 785 L2854 157" stroke-width="31"/></g></g><g data-id="p2" class="note"><text x="2854" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">10</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M3905 785 L3905 157" stroke-width="31"/></g></g><g data-id="p3" class="note"><text x="3905" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">12</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M4956 785 L4956 157" stroke-width="31"/></g></g><g data-id="p4" class="note"><text x="4956" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">10</tspan></text></g></g></g></g><g class="barLine"><path d="M6007 942 L6007 2512" stroke-width="27"/></g></g><g class="measure"><g class="staff"><path d="M6020 942 L6496 942" stroke-width="23"/><path d="M6748 942 L7547 942" stroke-width="23"/><path d="M7799 942 L8598 942" stroke-width="23"/><path d="M8850 942 L9649 942" stroke-width="23"/><path d="M9901 942 L11001 942" stroke-width="23"/><path d="M6020 1257 L11001 1257" stroke-width="23"/><path d="M6020 1572 L11001 1572" stroke-width="23"/><path d="M6020 1887 L11001 1887" stroke-width="23"/><path d="M6020 2202 L11001 2202" stroke-width="23"/><path d="M6020 2517 L11001 2517" stroke-width="23"/><g class="layer"><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M6622 785 L6622 157" stroke-width="31"/></g></g><g data-id="p5" class="note"><text x="6622" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">8</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M7673 785 L7673 157" stroke-width="31"/></g></g><g data-id="p6" class="note"><text x="7673" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">7</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M8724 785 L8724 157" stroke-width="31"/></g></g><g data-id="p7" class="note"><text x="8724" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">5</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M9775 785 L9775 157" stroke-width="31"/></g></g><g data-id="p8" class="note"><text x="9775" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">3</tspan></text></g></g></g></g><g class="barLine"><path d="M10826 942 L10826 2512" stroke-width="27"/><path d="M10956 942 L10956 2512" stroke-width="90"/></g></g><g class="systemMilestoneEnd yhbsl03"/></g><g class="pageMilestoneEnd wtn72u"/><g class="pageMilestoneEnd v3bpgbh"/></g></svg></svg><!-- /engrave:phrase-lowest --></div></div>
  <div class="tab-compare-row"><p class="tab-compare-name">換把最少</p><div class="notation-sheet"><!-- engrave:phrase-hand --><svg class="engraved" viewBox="0 0 1100 313" role="img" aria-label="這個樂句依換把最少寫成的六線譜：最後兩個音在 B 弦的第 10 格和第 8 格" focusable="false"><svg class="definition-scale" viewBox="0 0 11000 3130"><g class="page-margin" transform="translate(0, 80)"><g class="system"><g class="measure"><g class="staff"><path d="M0 942 L1677 942" stroke-width="23"/><path d="M1929 942 L2647 942" stroke-width="23"/><path d="M3061 942 L3698 942" stroke-width="23"/><path d="M4112 942 L4749 942" stroke-width="23"/><path d="M5163 942 L6020 942" stroke-width="23"/><path d="M0 1257 L6020 1257" stroke-width="23"/><path d="M0 1572 L6020 1572" stroke-width="23"/><path d="M0 1887 L6020 1887" stroke-width="23"/><path d="M0 2202 L6020 2202" stroke-width="23"/><path d="M0 2517 L6020 2517" stroke-width="23"/><g class="clef"><use href="#ss-E06D" transform="translate(159, 1727) scale(0.72, 0.72)"/></g><g class="meterSig"><use href="#ss-E084" transform="translate(752, 1547) scale(0.72, 0.72)"/><use href="#ss-E084" transform="translate(752, 1907) scale(0.72, 0.72)"/></g><g class="layer"><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M1803 785 L1803 157" stroke-width="31"/></g></g><g data-id="p1" class="note"><text x="1803" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">8</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M2854 785 L2854 157" stroke-width="31"/></g></g><g data-id="p2" class="note"><text x="2854" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">10</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M3905 785 L3905 157" stroke-width="31"/></g></g><g data-id="p3" class="note"><text x="3905" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">12</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M4956 785 L4956 157" stroke-width="31"/></g></g><g data-id="p4" class="note"><text x="4956" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">10</tspan></text></g></g></g></g><g class="barLine"><path d="M6007 942 L6007 2512" stroke-width="27"/></g></g><g class="measure"><g class="staff"><path d="M6020 942 L6496 942" stroke-width="23"/><path d="M6748 942 L7547 942" stroke-width="23"/><path d="M7799 942 L11001 942" stroke-width="23"/><path d="M6020 1257 L8517 1257" stroke-width="23"/><path d="M8931 1257 L9649 1257" stroke-width="23"/><path d="M9901 1257 L11001 1257" stroke-width="23"/><path d="M6020 1572 L11001 1572" stroke-width="23"/><path d="M6020 1887 L11001 1887" stroke-width="23"/><path d="M6020 2202 L11001 2202" stroke-width="23"/><path d="M6020 2517 L11001 2517" stroke-width="23"/><g class="layer"><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M6622 785 L6622 157" stroke-width="31"/></g></g><g data-id="p5" class="note"><text x="6622" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">8</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M7673 785 L7673 157" stroke-width="31"/></g></g><g data-id="p6" class="note"><text x="7673" y="1052" text-anchor="middle" font-size="0px"><tspan font-size="324px">7</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M8724 785 L8724 157" stroke-width="31"/></g></g><g data-id="p7" class="note is-marked"><text x="8724" y="1366" text-anchor="middle" font-size="0px"><tspan font-size="324px">10</tspan></text></g></g><g class="tabGrp"><g class="tabDurSym"><g class="stem"><path d="M9775 785 L9775 157" stroke-width="31"/></g></g><g data-id="p8" class="note is-marked"><text x="9775" y="1366" text-anchor="middle" font-size="0px"><tspan font-size="324px">8</tspan></text></g></g></g></g><g class="barLine"><path d="M10826 942 L10826 2512" stroke-width="27"/><path d="M10956 942 L10956 2512" stroke-width="90"/></g></g><g class="systemMilestoneEnd yhbsl03"/></g><g class="pageMilestoneEnd wtn72u"/><g class="pageMilestoneEnd v3bpgbh"/></g></svg></svg><!-- /engrave:phrase-hand --></div></div>
  <figcaption>同樣的八個音，由 ScoreStarling 依兩種規則分別畫出。紅色的兩個音移到了 B 弦上，手因此能停在第 7 到第 12 格之間。</figcaption>
</figure>
<!-- engrave:glyphs --><svg class="glyph-sprite" width="0" height="0" aria-hidden="true" focusable="false"><defs><g id="ss-E06D"><path transform="scale(1,-1)" d="M18 697v53h369v-53h-154v-418h-60v418h-155zM111 -94l-50 -141h-64l168 470h78l165 -470h-67l-49 141h-181zM276 -43l-73 214l-77 -214h150zM27 -750v471h191c89 0 139 -51 139 -120c0 -47 -22 -82 -65 -102c60 -23 86 -58 86 -114c0 -78 -54 -135 -139 -135h-212z M203 -332h-116v-150h116c67 0 94 27 94 75s-27 75 -94 75zM234 -535h-147v-162h147c56 0 84 36 84 81c0 46 -28 81 -84 81z"/></g><g id="ss-E084"><path transform="scale(1,-1)" d="M20 -78c84 97 114 180 134 329h170c-13 -32 -82 -132 -99 -151l-84 -97c-33 -36 -59 -63 -80 -81h162v102l127 123v-225h57v-39h-57v-34c0 -43 19 -65 57 -65v-34h-244v36c48 0 60 26 60 70v27h-203v39z"/></g></defs></svg><!-- /engrave:glyphs -->
<div class="table-scroll" role="region" aria-label="兩種選弦規則下的一個下行樂句" tabindex="0">
<table>
<caption>標準調音下從 C5 開始的樂句：弦與格，1 弦為音最高的那條弦</caption>
<thead><tr><th scope="col">音</th><th scope="col">低格優先</th><th scope="col">換把最少</th></tr></thead>
<tbody>
<tr><th scope="row">C5</th><td>1 弦第 8 格</td><td>1 弦第 8 格</td></tr>
<tr><th scope="row">D5</th><td>1 弦第 10 格</td><td>1 弦第 10 格</td></tr>
<tr><th scope="row">E5</th><td>1 弦第 12 格</td><td>1 弦第 12 格</td></tr>
<tr><th scope="row">D5</th><td>1 弦第 10 格</td><td>1 弦第 10 格</td></tr>
<tr><th scope="row">C5</th><td>1 弦第 8 格</td><td>1 弦第 8 格</td></tr>
<tr><th scope="row">B4</th><td>1 弦第 7 格</td><td>1 弦第 7 格</td></tr>
<tr><th scope="row">A4</th><td>1 弦第 5 格</td><td>2 弦第 10 格</td></tr>
<tr><th scope="row">G4</th><td>1 弦第 3 格</td><td>2 弦第 8 格</td></tr>
</tbody>
</table>
</div>
<p>低格優先的排法在一條弦上橫跨第 3 到第 12 格，下行時手要一次又一次地移動。換把最少的排法把最後兩個音放在 B 弦的第 10 格和第 8 格，手始終保持在第 7 到第 12 格之間。兩種都不算錯，大多數打譜軟體顯示的是低格優先的版本；第二種比較接近老師會寫的指法。</p>
<p>程式只要通盤規劃整個聲部，就能找到第二種排法。ScoreStarling 的「換把最少」選項把每個音或和弦當作一步，列出它所有可能的按法，再在所有步驟之間搜尋代價最小的路徑：手一次涵蓋四格；換把移得越遠，代價越高；跨度超過四格的和弦要額外加代價；高把位和第 12 格以上的位置稍微加一點；空弦不計代價。不過它還是不了解你的演奏技巧，所以規劃出來的指法只是建議，仍需要你自己確認。</p>

<h2 id="octave-too-high">為什麼我的六線譜高了一個八度？</h2>
<p>因為吉他的實際音高比記譜低一個八度，而檔案有兩種方式表達這一點。MusicXML 可以按實際音高記譜，使用下方帶小 8 的高音譜號；也可以高八度記譜，再加上 <code>transpose</code> 元素，它「表示記譜音高需要加上多少，才能得到正確的實際音高」。兩種方式印出來的五線譜一模一樣。如果軟體依照第二種檔案的記譜音高來填六線譜，每個音都會高一個八度。</p>
<p>我們在 MuseScore 4.7.5 裡親眼看過。一個檔案裡是開放把位的 C 大調音階，實際音高從 C3 到 C4（從 A 弦第 3 格到 B 弦第 1 格），按第二種方式記譜，並寫明了弦和格。MuseScore 把所有格數都換掉了，寫成從 B 弦第 1 格到 1 弦第 8 格——同一條音階，高了一個八度。改用帶 8 的譜號按實際音高記譜後，同一條音階開啟時，每條弦、每個格數都和檔案裡寫的一致。ScoreStarling 下載的 MusicXML 就是這樣寫六線譜聲部的。</p>
<p class="note">如果別人給你的六線譜高了一個八度，先別急著改音，檢查一下原始檔案裡這個聲部的譜號和移調設定。</p>

<h2 id="capo">移調夾在檔案裡改變了什麼？</h2>
<p>改變的是空弦音。MusicXML 的 <code>capo</code> 元素會依它的半音數「改變各弦的空弦調音」，而格數從空弦的 0 開始編號，所以移調夾夾在第 2 格時，0 格就是在移調夾處彈響的弦，第 3 格則是從上弦枕數起的第 5 格。這和吉他手看標了移調夾的彈唱譜時的讀法一致。MuseScore 4.7.5 開啟我們的 MusicXML 時忽略了移調夾，從上弦枕開始標格數，所以最好也在樂譜的文字說明裡註明移調夾的位置。</p>

<h2 id="scorestarling">ScoreStarling 怎麼寫六線譜</h2>
<p>從 2026年10月8日起，ScoreStarling 可以把吉他、貝斯或烏克麗麗聲部顯示為五線譜加下方的 TAB 譜，或只顯示 TAB 譜，來源可以是錄音或影片連結、PDF 樂譜，或是 MusicXML、MIDI、ABC 檔案。預設的弦位遵循上文的低格優先規則；「換把最少」會幫你規劃，讓手盡量停在一個把位；你也可以把任何一個音移到另一條弦上（音高不變），或直接輸入格數。調音包括 Drop D、DADGAD 和開放 G，移調夾最高可以夾到第 12 格。任何弦都無法照原樣彈出的音會被標出。它不記推弦、滑音、搥弦和刷弦，整首曲子也只用一種調音。可以到<a href="https://scorestarling.com/zh-hant/audio-to-guitar-tab">音訊轉吉他譜</a>、<a href="https://scorestarling.com/zh-hant/youtube-to-guitar-tab">YouTube 影片轉吉他譜</a>或<a href="https://scorestarling.com/zh-hant/sheet-music-to-guitar-tab">五線譜轉吉他譜</a>頁面試試看。</p>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://github.com/musescore/MuseScore/blob/master/src/engraving/dom/stringdata.cpp">MuseScore，<code>src/engraving/dom/stringdata.cpp</code></a>：<code>convertPitch</code> 和 <code>fretChords</code>，2026年10月7日查閱。</li>
<li><a href="https://musicxml.formats.music/musicxml-reference/elements/string/">MusicXML 4.0 的 string 元素</a>：1 弦是全長的弦中音最高的一條。</li>
<li><a href="https://musicxml.formats.music/musicxml-reference/elements/fret/">MusicXML 4.0 的 fret 元素</a>：空弦記為 0。</li>
<li><a href="https://musicxml.formats.music/musicxml-reference/elements/capo/">MusicXML 4.0 的 capo 元素</a>.</li>
<li><a href="https://musicxml.formats.music/musicxml-reference/elements/transpose/">MusicXML 4.0 的 transpose 元素</a>.</li>
<li>我們的比較測試：標準調音下的 480 個隨機和弦，MuseScore Studio 4.7.5 命令列與 ScoreStarling 的比較；以及用兩種方式記譜的 C 大調音階。2026年10月8日。</li>
</ol>
</section><h2>常見問題</h2><h3>為什麼打譜軟體把音排在我不會用的那條弦上？</h3><p>因為五線譜不會標明用哪條弦，軟體只能照規則來排。MuseScore 會選能彈出這個音的音最高的那條弦，也就是最低的格；和弦裡某個音要用的弦如果已被佔用，就順延到下一條空著的弦。你習慣在別處彈的音，自己改到對應的弦上就好，音高不會變。</p><h3>為什麼我的吉他六線譜高了一個八度？</h3><p>很可能是檔案用 transpose 元素，把吉他記得比實際音高高一個八度，而軟體依照記譜音高排了格數。改成按實際音高記譜、使用下方帶 8 的高音譜號，就能解決。在我們的測試中，用第一種方式記譜的開放把位 C 大調音階，被 MuseScore 4.7.5 讀成了高一個八度、排在 1、2 弦上的同一條音階。</p><h3>夾了移調夾，0 格指的是上弦枕還是移調夾？</h3><p>在 MusicXML 裡指的是移調夾：capo 元素會把各弦的空弦音升高相應的半音數，所以 0 格就是在移調夾處彈響的弦。MuseScore 4.7.5 開啟我們的 MusicXML 時忽略了移調夾，從上弦枕開始數格。</p><h3>ScoreStarling 能把錄音扒成吉他六線譜嗎？</h3><p>可以，2026年10月8日起就支援：它會把錄音寫成五線譜並在下方配上六線譜，也可以只顯示六線譜，適用於吉他、貝斯和烏克麗麗，單一樂器免費。它不記推弦、滑音和搥弦。詳見<a href="https://scorestarling.com/zh-hant/audio-to-guitar-tab">音訊轉吉他譜</a>。</p>]]></content:encoded>
    </item>
    <item>
      <title>MP3 會影響扒譜準確率嗎？WAV 與 MP3 實測比較</title>
      <link>https://scorestarling.com/zh-hant/blog/mp3-vs-wav-music-transcription</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/mp3-vs-wav-music-transcription</guid>
      <pubDate>Wed, 07 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>研究</category>
      <description>基本上不會。我們用三個轉譜引擎測試了同一批錄音的 WAV、MP3 和 AAC 版本：320 kbit/s 的 MP3 讓起音 F1 最多只變動 0.002，每個檔案的長度也完全不變。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>320 kbit/s 的 MP3 不會。2026年10月5日，我們用三個引擎分別轉了同一批錄音的無損版本，以及經過 MP3 和 AAC 編碼再解碼的版本：Spotify 的 Basic Pitch 處理 12 段吉他錄音，一個鋼琴獨奏模型處理 50 段鋼琴演奏，Mirelo 處理一首樂團歌曲。320 kbit/s 的 MP3 讓每個引擎的起音 F1（範圍 0 到 1）最多只變動 0.002，相當於幾千個音符裡只變了寥寥幾個，而且每個檔案的長度精確到取樣點、分毫不差。192 kbit/s 的 MP3 和 256 kbit/s 的 AAC 讓分數最多變動 0.008，升高和降低的次數差不多；AAC 檔案解碼後最多長了 733 個取樣點。</p>
</section>

<h2 id="why-ask">MP3 為什麼會改變音符？</h2>
<p>MP3 和 AAC 會丟掉人耳不太容易察覺的細節，主要是緊鄰較大聲響的微弱聲音，以及高頻部分。Basic Pitch 這類模型讀取的是音訊的時頻圖，誰也不能保證它們會像聽眾一樣忽略這些細節。一個落在模型門檻附近的音符，可能因為頻譜的細微變化而出現或消失。</p>
<p>過去的研究顯示影響很小。Urbano 等人發現，位元率從約 160 kbit/s 起，MFCC 特徵在有損編碼下就保持穩定；而反映音級的色度（chroma）特徵幾乎完全不受編解碼器和位元率影響（ISMIR 2014）。他們測的是特徵，不是轉譜得到的音符，所以我們測了音符。</p>
<p>我們也有實際的考量。錄音及其播放音訊的無損副本占了樂譜儲存空間的 77%，我們想改存較小的副本，但前提是準確率絲毫不受影響。</p>

<h2 id="how-we-tested">我們怎麼測的</h2>
<p>每段錄音都用 FFmpeg 8.1.1 分別編碼（LAME 用 320 或 192 kbit/s 的固定位元率，FFmpeg 內建的 AAC 編碼器用 256 kbit/s），再用讀取上傳檔案的同一套程式碼解碼。對照組把無損音訊改動 0.01 dB——誰也聽不出來——用來顯示完全不經過編解碼器時，各項指標本身會浮動多少。三個引擎的結果都是確定的：相同的輸入一定得到相同的音符。</p>
<div class="table-scroll" role="region" aria-label="每個引擎的測試內容" tabindex="0">
<table>
<caption>引擎與錄音</caption>
<thead>
<tr><th scope="col">引擎</th><th scope="col">錄音</th><th scope="col">參考標註</th></tr>
</thead>
<tbody>
<tr><th scope="row">Basic Pitch（ScoreStarling 處理單一樂器時的用法）</th><td>12 段 GuitarSet 錄音（六位演奏者各一段獨奏、一段伴奏），Slakh2100 中一首樂團歌曲的前 60 秒，以及一首我們自己創作並合成的 58 秒樂團作品</td><td>資料集附帶的標註</td></tr>
<tr><th scope="row">Kong 等人的鋼琴獨奏模型</th><td>25 首鋼琴曲（24 首公共領域樂譜，外加 1 首我們自己寫的），每首以不同的彈性速度演奏兩遍，並以 SoundFont 鋼琴音色加延音踏板合成音訊：共 50 段演奏</td><td>實際演奏的音符</td></tr>
<tr><th scope="row">Mirelo 的 Audio-to-MIDI（a2m-1.1）</th><td>同一首 Slakh 樂團歌曲的 60 秒片段，只測了無損和 MP3 320</td><td>這首歌的 MIDI</td></tr>
</tbody>
</table>
</div>
<p>起音 F1（onset F1）的判定方法是：轉出的音符與參考標註中某個音符的音高相差不超過 50 音分、起點相差不超過 50 毫秒，就算轉對；漏掉的音和多出來的音扣分相同。1.0 為滿分。《<a href="https://scorestarling.com/zh-hant/blog/ai-music-transcription-accuracy">AI 扒譜到底有多準？我們實測了一下</a>》一文對這個指標有完整說明。</p>

<h2 id="basic-pitch-results">單一樂器的結果變了嗎？</h2>
<p>幾乎沒變。在 12 段吉他錄音上，Basic Pitch 的起音 F1 在每種格式下都保持在 0.761 到 0.764 之間，而立體聲檔案縮小到了 WAV 的 14% 到 23%。</p>
<figure class="bars-figure">
<div class="bars" aria-hidden="true">
<p class="bars-title">起音 F1，12 段吉他錄音 <span>0 到 1，越高越好</span></p>
<div class="bar-row"><span>無損</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="76.26%" height="10" rx="3"/></svg><b>0.763</b></div>
<div class="bar-row"><span>對照</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="76.27%" height="10" rx="3"/></svg><b>0.763</b></div>
<div class="bar-row"><span>MP3 320</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="76.11%" height="10" rx="3"/></svg><b>0.761</b></div>
<div class="bar-row"><span>AAC 256</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="76.44%" height="10" rx="3"/></svg><b>0.764</b></div>
<div class="bar-row"><span>MP3 192</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="76.16%" height="10" rx="3"/></svg><b>0.762</b></div>
</div>
<div class="bars" aria-hidden="true">
<p class="bars-title">立體聲錄音的檔案大小 <span>占 WAV 的比例</span></p>
<div class="bar-row"><span>無損</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="100%" height="10" rx="3"/></svg><b>100%</b></div>
<div class="bar-row"><span>對照</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="100%" height="10" rx="3"/></svg><b>100%</b></div>
<div class="bar-row"><span>MP3 320</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="23%" height="10" rx="3"/></svg><b>23%</b></div>
<div class="bar-row"><span>AAC 256</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="17%" height="10" rx="3"/></svg><b>17%</b></div>
<div class="bar-row"><span>MP3 192</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="14%" height="10" rx="3"/></svg><b>14%</b></div>
</div>
<figcaption>同樣 12 段 GuitarSet 錄音的五種格式：檔案縮小到四分之一甚至更小，起音 F1 卻幾乎沒動。詳細數值見下表。</figcaption>
</figure>
<div class="table-scroll" role="region" aria-label="Basic Pitch 在各格式下的結果" tabindex="0">
<table>
<caption>ScoreStarling 所用的 Basic Pitch 在各格式下的結果（2026年10月5日）</caption>
<thead>
<tr><th scope="col">指標</th><th scope="col" class="num">無損</th><th scope="col" class="num">對照</th><th scope="col" class="num">MP3 320</th><th scope="col" class="num">AAC 256</th><th scope="col" class="num">MP3 192</th></tr>
</thead>
<tbody>
<tr><th scope="row">起音 F1，吉他</th><td class="num">0.7626</td><td class="num">0.7627</td><td class="num">0.7611</td><td class="num">0.7644</td><td class="num">0.7616</td></tr>
<tr><th scope="row">起止 F1，吉他</th><td class="num">0.5224</td><td class="num">0.5222</td><td class="num">0.5212</td><td class="num">0.5228</td><td class="num">0.5210</td></tr>
<tr><th scope="row">起音 F1，Slakh 歌曲</th><td class="num">0.5204</td><td class="num">0.5210</td><td class="num">0.5204</td><td class="num">0.5240</td><td class="num">0.5227</td></tr>
<tr><th scope="row">起音 F1，我們的樂團作品</th><td class="num">0.7136</td><td class="num">0.7136</td><td class="num">0.7123</td><td class="num">0.7089</td><td class="num">0.7054</td></tr>
<tr><th scope="row">找到的音符（共 3,893 個）</th><td class="num">2,741</td><td class="num">2,742</td><td class="num">2,739</td><td class="num">2,744</td><td class="num">2,737</td></tr>
<tr><th scope="row">其中有變化的音符</th><td class="num">—</td><td class="num">1</td><td class="num">10</td><td class="num">17</td><td class="num">14</td></tr>
<tr><th scope="row">調與標註一致</th><td class="num">12 段中 10 段</td><td class="num">12 段中 10 段</td><td class="num">12 段中 10 段</td><td class="num">12 段中 10 段</td><td class="num">12 段中 10 段</td></tr>
<tr><th scope="row">速度不變</th><td class="num">—</td><td class="num">14 段中 14 段</td><td class="num">14 段中 14 段</td><td class="num">14 段中 14 段</td><td class="num">14 段中 14 段</td></tr>
<tr><th scope="row">長度變化（取樣點）</th><td class="num">—</td><td class="num">0</td><td class="num">0</td><td class="num">+16 到 +733</td><td class="num">0</td></tr>
<tr><th scope="row">訊雜比，dB（中位數）</th><td class="num">—</td><td class="num">58.4</td><td class="num">53.2</td><td class="num">39.4</td><td class="num">30.3</td></tr>
<tr><th scope="row">檔案大小，單聲道 / 立體聲</th><td class="num">100%</td><td class="num">100%</td><td class="num">45% / 23%</td><td class="num">23% / 17%</td><td class="num">27% / 14%</td></tr>
</tbody>
</table>
</div>
<p class="note">無損版本的分數是 0.763，而不是我們 10月3日那篇文章中的 0.744：從 10月4日起，ScoreStarling 把單一樂器寫成一條旋律線，這改變了其中五段獨奏的結果。兩次測試用的都是 Basic Pitch 0.4.0。</p>

<h2 id="piano-results">鋼琴獨奏呢？</h2>
<p>鋼琴模型在每種格式下聽出的音符都一樣。以實際演奏的音符為參照，它的起音 F1 在無損版本上為 0.9427，對照組為 0.9426，MP3 320 為 0.9428，MP3 192 為 0.9433；每種格式下都找到了約 92.2% 的譜面音符。</p>
<p>根據這些音符判斷拍子和小節線的那一步就沒那麼穩定了，但原因不在編解碼器。經過人耳聽不出的對照改動後，50 段演奏裡有 5 段的小節線位置變了；經過 MP3 320 後有 7 段，其中 4 段和對照組是同樣的演奏。這一步對任何改動都很敏感，所以我們把它視為測量本身的雜訊，而不是 MP3 的代價。鋼琴沒有測試 AAC。</p>

<h2 id="band-results">整個樂團呢？</h2>
<p>我們用 Mirelo 做了一次付費比較，測的是那首 60 秒的樂團歌曲：無損檔案和 MP3 都回傳了 842 個音符和鼓點，其中 829 個（98.5%）的起點與無損版本相差不到 50 毫秒。</p>
<div class="table-scroll" role="region" aria-label="Mirelo 的無損與 MP3 結果" tabindex="0">
<table>
<caption>Mirelo Audio-to-MIDI 處理一首 60 秒樂團歌曲的結果</caption>
<thead>
<tr><th scope="col">指標</th><th scope="col" class="num">無損</th><th scope="col" class="num">MP3 320</th></tr>
</thead>
<tbody>
<tr><th scope="row">起音 F1，有音高的音符（全曲 703 個）</th><td class="num">0.6707</td><td class="num">0.6687</td></tr>
<tr><th scope="row">鼓的起音 F1（423 個鼓點）</th><td class="num">0.6983</td><td class="num">0.7023</td></tr>
<tr><th scope="row">貝斯 / 吉他 / 鋼琴的起音 F1</th><td class="num">0.455 / 0.681 / 0.827</td><td class="num">0.471 / 0.678 / 0.832</td></tr>
<tr><th scope="row">速度，BPM</th><td class="num">133.0</td><td class="num">133.0</td></tr>
</tbody>
</table>
</div>

<h2 id="why-notes-moved">為什麼還是有音符變了？</h2>
<p>因為輸入只要有任何變化，就會牽動少數剛好卡在模型門檻上的音符。在 Basic Pitch 找到的約 2,740 個音符中，聽不出差別的對照改動了 1 個，MP3 320 改動了 10 個，AAC 改動了 17 個，MP3 192 改動了 14 個，單段錄音裡最多不超過 7 個。五組比較中，MP3 320 有三組略低（低 0.0013 到 0.0020），一組持平，一組略高。AAC 和 MP3 192 在吉他或 Slakh 歌曲上有所上升，在我們合成的樂團作品上有所下降，幅度最多 0.008。如果某種編解碼器真的損害了準確率，應該會在所有測試上都明顯下降，但沒有一種是這樣。</p>
<p>編解碼器確實會改變的，是頭尾的時間。AAC 編碼器會在開頭加入靜音的預置取樣，並把結尾補齊成一個完整的音框；Apple 的文件寫明延遲為 2112 個取樣點。我們的 AAC 檔案解碼後長了 16 到 733 個取樣點。FFmpeg 寫出的 MP3 帶有記錄編碼器延遲的 Xing/LAME 標頭，我們的 MP3 檔案解碼後與原始長度完全一致。當樂譜要和錄音同步播放時，這一點很重要。</p>

<h2 id="which-file">該上傳哪種檔案？</h2>
<ul>
<li>手上有什麼就傳什麼。先把 WAV 轉成 MP3 得不到任何好處，把 MP3 轉回 WAV 也找不回任何東西。</li>
<li>如果一定要壓縮，320 kbit/s 的 MP3 在訊雜比上最接近原始檔案，而且長度完全不變。</li>
<li>低位元率沒有測過。我們只測到 192 kbit/s，所以請留意你的錄音 App 儲存的是什麼格式和位元率。</li>
<li>錄音品質遠比格式重要：一個清楚的聲部，殘響少，沒有背景音樂。要檢查哪些地方，請見《<a href="https://scorestarling.com/zh-hant/blog/voice-memo-to-sheet-music">如何把語音備忘錄轉成樂譜</a>》。</li>
</ul>
<p>ScoreStarling 已經據此做了調整：從 2026年10月5日起，上傳的 WAV、AIFF 或 FLAC 檔案會儲存為 320 kbit/s 的 MP3，並用這份副本轉譜，立體聲錄音的儲存空間因此降到 WAV 的 23%。MP3、M4A、Ogg 和影片檔案則保留原檔。</p>

<h2 id="limits">這次測試的侷限</h2>
<ul>
<li>Mirelo 只測了一首樂團歌曲；鋼琴用的是合成音訊，不是真實錄製的鋼琴。</li>
<li>沒有測人聲。我們手上的真實錄音屬於私人錄音，而且本來就是 AAC 檔案，沒辦法作為無損參照。</li>
<li>每種格式只用了一個編碼器，也沒有測低於 192 kbit/s 的位元率。</li>
<li>起音 F1 只說明音符有沒有被聽出來，不說明寫出的樂譜好不好讀。</li>
</ul>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://archives.ismir.net/ismir2014/paper/000326.pdf">What Is the Effect of Audio Quality on the Robustness of MFCCs and Chroma Features?</a>——Urbano、Bogdanov、Herrera、Gómez 和 Serra，ISMIR 2014</li>
<li><a href="https://zenodo.org/records/3371780">GuitarSet 1.1.0</a>——Xi、Bittner、Pauwels、Ye 和 Bello，Zenodo（CC BY 4.0）</li>
<li><a href="https://zenodo.org/records/4599666">Slakh2100</a>——Manilow、Wichern、Seetharaman 和 Le Roux，Zenodo（CC BY 4.0）</li>
<li><a href="https://arxiv.org/abs/2203.09893">A Lightweight Instrument-Agnostic Model for Polyphonic Note Transcription and Multipitch Estimation</a>——Bittner 等人，Spotify，2022（Basic Pitch）</li>
<li><a href="https://arxiv.org/abs/2010.01815">High-resolution Piano Transcription with Pedals by Regressing Onset and Offset Times</a>——Kong、Li、Song、Wan 和 Wang，2020</li>
<li><a href="https://mir-eval.readthedocs.io/latest/api/transcription.html">mir_eval.transcription</a>——mir_eval 文件</li>
<li><a href="https://ffmpeg.org/ffmpeg-formats.html#mp3">FFmpeg formats: the mp3 muxer</a>——FFmpeg 文件</li>
<li><a href="https://developer.apple.com/library/archive/technotes/tn2258/_index.html">TN2258: AAC Audio, Encoder Delay and Synchronization</a>——Apple</li>
</ol>
</section><h2>常見問題</h2><h3>扒譜該上傳 WAV 還是 MP3？</h3><p>都可以。在我們 2026年10月5日的測試中，用三個不同的引擎轉譜，320 kbit/s 的 MP3 與其來源 WAV 的起音 F1 相差都不超過 0.002。錄音裝置或編輯軟體給你什麼檔案，就直接上傳，不用轉檔。192 kbit/s 的 MP3 和 256 kbit/s 的 AAC 相差在 0.008 以內；更低的位元率沒有測試。</p><h3>MP3 位元率更低，音符會變嗎？</h3><p>會變幾個，有升有降。在 14 段錄音約 2,740 個比對成功的音符中，192 kbit/s 時有 14 個發生變化，320 kbit/s 時有 10 個，而一次人耳聽不出的音量改動也讓 1 個變了。起音 F1 在 192 kbit/s 時最多變動 0.008，在 320 kbit/s 時最多變動 0.002。低於 192 kbit/s 的位元率沒有測量。</p><h3>為什麼 AAC 檔案會比 WAV 稍長一點？</h3><p>AAC 編碼器會在開頭加入一段靜音的預置取樣（priming samples），並把結尾補齊成一個完整的音框（frame）；Apple 的文件寫明預置延遲為 2112 個取樣點。如果播放器或解碼器沒有把它們裁掉，音訊就會稍長一些。在我們的測試中，AAC 檔案解碼後長了 16 到 733 個取樣點。FFmpeg 寫出、帶有 Xing/LAME 標頭的 MP3，解碼後長度完全一致。</p><h3>ScoreStarling 會把我的 WAV 轉成 MP3 嗎？</h3><p>會。從 2026年10月5日起，上傳的 WAV、AIFF 或 FLAC 檔案會儲存為 320 kbit/s 的 MP3（也就是本文測試的設定），並用這份副本轉譜。上傳的 MP3、M4A、Ogg 和影片則保留原檔。</p>]]></content:encoded>
    </item>
    <item>
      <title>手機版 ChatGPT 中的 MCP App：我們的實測心得</title>
      <link>https://scorestarling.com/zh-hant/blog/mcp-app-chatgpt-mobile</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/mcp-app-chatgpt-mobile</guid>
      <pubDate>Wed, 07 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>技術</category>
      <description>在手機上，ChatGPT 把輸入框算進約 127 px 的底部邊距，還會用舊結果重建我們的 MCP App。本文記錄四個 bug、對應的修正，以及能抓出它們的測試。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>在手機上，ChatGPT 會把標題列和輸入框疊在全螢幕的 MCP App 上方，並以安全區域邊距的形式回報，我們實測分別約為 47 px 和 127 px；應用程式開啟期間，底部邊距還會變動。每次進入或離開全螢幕，它也會用對話中最初的工具結果重建我們的面板。因應方式是：把邊距區域留空，但不要再加自己的餘量；每次上下文變化時都重新定位用腳本定位的元素；載入時檢查結果是否已經過時。我們測的是 iPhone 模擬器中的 ChatGPT 手機網頁版，不是原生 App。</p>
</section>

<h2 id="what-broke-on-a-phone">在手機版 ChatGPT 中開啟我們的 MCP App，出了哪些問題？</h2>
<p>四個問題，都和全螢幕有關。ScoreStarling 的面板會在對話中顯示樂譜（<a href="https://scorestarling.com/zh-hant/mcp">連線方式</a>）；全螢幕時可以點選音符，再從選取卡片就這些音符向對話提問，在手機上這張卡片會停靠在播放器上方。2026年10月7日，我們在 iOS 模擬器的 iPhone 17（402 × 874 點）上，用 Safari 開啟 ChatGPT 手機網頁版來執行它。面板所在的 iframe 是跨來源的，所以我們只能從螢幕截圖判讀它的狀態。</p>
<div class="table-scroll" role="region" aria-label="在手機版 ChatGPT 上發現的問題" tabindex="0">
<table>
<caption>在 ChatGPT 手機網頁版上發現的問題（2026年10月7日）</caption>
<thead><tr><th scope="col">現象</th><th scope="col">原因</th><th scope="col">修正</th></tr></thead>
<tbody>
<tr><th scope="row">播放器浮到了螢幕約三分之一高的位置</th><td>底部邊距（約 127 px）已涵蓋輸入框，我們又在上面固定多留 96 px</td><td>取邊距和 96 px 中較大的那個</td></tr>
<tr><th scope="row">草稿變長或出現 Thinking 狀態列時，播放器往上移了，停靠的卡片卻沒動</th><td>ChatGPT 改變了邊距；播放器靠 CSS 跟著移動，卡片卻由腳本定位</td><td>每次上下文變化時重新定位卡片</td></tr>
<tr><th scope="row">編輯後進入或離開全螢幕，顯示的是舊樂譜，而且是唯讀</th><td>ChatGPT 用最初的工具結果建立了新面板</td><td>結果落後時讀取目前版本</td></tr>
<tr><th scope="row">在對話中編輯後，卡片懸在播放器上方約 40 px 處</th><td>我們的「Updated from the chat」提示移動了卡片定位所參照的區域</td><td>該區域尺寸改變時重新定位卡片</td></tr>
</tbody>
</table>
</div>

<h2 id="message-box-safe-area">在手機上，全螢幕的 MCP App 為什麼會被 ChatGPT 的輸入框擋住？</h2>
<p>因為 ChatGPT 把輸入框畫在你的 iframe 上方，並透過底部安全區域邊距告訴你它擋住了多少。忽略這個邊距，你的控制項就會被輸入框蓋住；像我們那樣在上面再加一段自己的餘量，控制項又會懸在半空。MCP Apps 規範對 <code>safeAreaInsets</code> 的描述只有一句「Safe area boundaries in pixels」（以像素為單位的安全區域邊界），OpenAI 的文件則說全螢幕時輸入框（composer）仍會疊在上方，但沒有給出尺寸（兩者皆於 2026年10月7日確認）。我們的 47 px 和 127 px 是從螢幕截圖估算的。</p>
<p>10月6日，手機上的一次對話顯示我們的播放器被 ChatGPT 的標題列擋住，於是我們把標頭列移到頂部邊距下方，把播放器移到底部；在底部，我們的 CSS 還在底部邊距之上另外為輸入框留了 96 px。本機用 34 px 邊距測試時看起來沒問題，換成 ChatGPT 的 127 + 96 就不對了。現在面板會取兩者中較大的那個，同時也讀取 ChatGPT 的 <code>window.openai.safeArea</code>：</p>
<pre><code>// panel.js, simplified: the host's inset or 96 px, never both
const bottom = Math.max(context.safeAreaInsets?.bottom || 0, window.openai?.safeArea?.insets?.bottom || 0);
root.style.setProperty('--safe-bottom', `${bottom}px`);
root.style.setProperty('--composer-space', fullscreen ? `${Math.max(0, 96 - bottom)}px` : '0px');
/* panel.css */
.stage &gt; .player { bottom: calc(10px + var(--safe-bottom) + var(--composer-space)); }</code></pre>
<figure class="diagram">
<svg viewBox="0 0 320 356" aria-hidden="true" focusable="false">
<rect class="d-box" x="10" y="8" width="140" height="304" rx="12"/>
<path class="d-line" d="M10 40.5H150"/>
<path class="d-line" d="M20 52H140M20 55H140M20 58H140M20 61H140M20 64H140"/>
<path class="d-line" d="M20 82H140M20 85H140M20 88H140M20 91H140M20 94H140"/>
<path class="d-line" d="M20 112H140M20 115H140M20 118H140M20 121H140M20 124H140"/>
<path class="d-line" d="M20 142H140M20 145H140M20 148H140M20 151H140M20 154H140"/>
<path class="d-line" d="M20 172H140M20 175H140M20 178H140M20 181H140M20 184H140"/>
<rect class="d-box d-accent d-dash" x="13" y="234.4" width="134" height="33.4" rx="4"/>
<text class="d-small d-mark" x="80" y="255" text-anchor="middle">+ 96 px</text>
<rect class="d-box" x="13" y="200.9" width="134" height="30" rx="9"/>
<circle class="d-mark" cx="28" cy="215.9" r="7"/>
<path class="d-line" d="M42 215.9H138"/>
<rect class="d-mark" x="42" y="214.9" width="28" height="2" rx="1"/>
<path class="d-shade" d="M10 20A12 12 0 0 1 22 8H138A12 12 0 0 1 150 20V24.4H10Z"/>
<text class="d-small" x="80" y="20" text-anchor="middle">標題列 · 47 px</text>
<path class="d-shade" d="M10 267.8H150V300A12 12 0 0 1 138 312H22A12 12 0 0 1 10 300Z"/>
<rect class="d-box" x="18" y="273" width="124" height="18" rx="9"/>
<text class="d-small" x="27" y="285.8">Ask ScoreStarling</text>
<text class="d-small" x="80" y="305" text-anchor="middle">輸入框 · 127 px</text>
<rect class="d-frame" x="10" y="8" width="140" height="304" rx="12"/>
<text class="d-text" x="80" y="334" text-anchor="middle">修正前</text>
<text class="d-small" x="80" y="349" text-anchor="middle">留空 127 + 96 px</text>
<rect class="d-box" x="170" y="8" width="140" height="304" rx="12"/>
<path class="d-line" d="M170 40.5H310"/>
<path class="d-line" d="M180 52H300M180 55H300M180 58H300M180 61H300M180 64H300"/>
<path class="d-line" d="M180 82H300M180 85H300M180 88H300M180 91H300M180 94H300"/>
<path class="d-line" d="M180 112H300M180 115H300M180 118H300M180 121H300M180 124H300"/>
<path class="d-line" d="M180 142H300M180 145H300M180 148H300M180 151H300M180 154H300"/>
<path class="d-line" d="M180 172H300M180 175H300M180 178H300M180 181H300M180 184H300"/>
<path class="d-line" d="M180 202H300M180 205H300M180 208H300M180 211H300M180 214H300"/>
<rect class="d-box" x="173" y="234.3" width="134" height="30" rx="9"/>
<circle class="d-mark" cx="188" cy="249.3" r="7"/>
<path class="d-line" d="M202 249.3H298"/>
<rect class="d-mark" x="202" y="248.3" width="28" height="2" rx="1"/>
<path class="d-shade" d="M170 20A12 12 0 0 1 182 8H298A12 12 0 0 1 310 20V24.4H170Z"/>
<text class="d-small" x="240" y="20" text-anchor="middle">標題列 · 47 px</text>
<path class="d-shade" d="M170 267.8H310V300A12 12 0 0 1 298 312H182A12 12 0 0 1 170 300Z"/>
<rect class="d-box" x="178" y="273" width="124" height="18" rx="9"/>
<text class="d-small" x="187" y="285.8">Ask ScoreStarling</text>
<text class="d-small" x="240" y="305" text-anchor="middle">輸入框 · 127 px</text>
<rect class="d-frame" x="170" y="8" width="140" height="304" rx="12"/>
<text class="d-text" x="240" y="334" text-anchor="middle">修正後</text>
<text class="d-small" x="240" y="349" text-anchor="middle">留空 127 px</text>
</svg>
<figcaption>我們的面板在 402 × 874 點的手機上（大致依比例），上方是 ChatGPT 的標題列（約 47 px），下方是輸入框（約 127 px）。修正前，面板在邊距之上又多留了 96 px；現在只留出邊距，宿主回報的邊距小於 96 px 時才留 96 px。</figcaption>
</figure>

<h2 id="host-context-changed">手機上全螢幕的 MCP App 開啟期間，會有哪些變化？</h2>
<p>邊距會變。我們實測時，草稿增加到兩行，或 Thinking 狀態列取代輸入框時，底部邊緣都會移動。規範允許宿主在任何上下文欄位變化時送出 <code>ui/notifications/host-context-changed</code>，只帶有變化的欄位，由畫面自行合併。</p>
<p>我們的播放器位置綁定在一個 CSS 變數上，跟著變了。選取卡片由腳本定位，留在原地沒動，所以現在每次上下文變化都會重新定位它。我們自己的提示又造成第二種偏移：「Updated from the chat」會在樂譜上方顯示 20 秒，把樂譜區域往下推。卡片的位置是從這個區域的頂端算起，於是滑到了播放器上面；如果這段期間有其他東西重新定位了它，提示消失後它又會懸在播放器上方。現在我們在這個區域加了一個 <code>ResizeObserver</code>，同樣會重新定位卡片，每個動畫影格最多一次：</p>
<pre><code>this.context = {...this.context, ...params};  // host.js, simplified: merge the partial update
function hostContext(next) { applyContext(next); editor.queuePlace(); }
new ResizeObserver(() =&gt; { updateClip(); editor.queuePlace(); }).observe($('stage'));</code></pre>

<h2 id="rebuilt-from-old-result">離開全螢幕後，MCP App 為什麼顯示的是舊版本？</h2>
<p>因為手機上的 ChatGPT 每次進入或離開全螢幕都會建立新的面板，交給它的是對話中最初的工具結果，也就是在任何編輯之前產生的那一份。規範允許宿主隨時銷毀畫面；而工具結果只記錄了工具執行那一刻的狀態。</p>
<p>我們的工具結果一半是快照、一半是即時資料：頁面圖片和音符對應它自己的修訂版本，另有一個「目前修訂版本」欄位，在面板讀取結果時才取得。重建後的面板畫出的是修訂版本 0，發現樂譜已經到了修訂版本 3，於是變成唯讀。它偵測對話編輯時，是拿伺服器的修訂版本和那個本來就是最新的欄位比較，所以永遠追不上。現在，如果宿主交給面板的結果已經落後於樂譜，面板會讀取一次目前的修訂版本；唯一的例外是結果中帶有等待核准的修改建議，這種結果本來就應該與目前版本不同。</p>

<h2 id="test-without-a-phone">不用手機，要怎麼測試 ChatGPT 在手機上的行為？</h2>
<p>在仿真環境中模仿真實宿主，並證明每個測試在拿掉修正後都會失敗。我們用的是 sunpeak，它為 Playwright 測試仿製了 ChatGPT 和 Claude。它的手機版 ChatGPT 介面（0.20.91 版）會畫出標題列和輸入框，並把輸入框回報為 92 px 的邊距。我們為這幾個 bug 寫的三個測試在 402 × 874 的觸控螢幕上執行，補上了其餘部分：它們透過這個介面的沙箱 iframe（它和 ChatGPT 的 iframe 一樣，是面板的上層）依序送出 127、160、70 和 127 px 的新邊距；編輯之後重新載入面板，再把最初的工具結果交給它。另一個測試則像對話那樣，在面板之外編輯樂譜。播放器最後必須位於「邊距與 96 px 中較大者」上方 0–24 px 處，卡片必須位於播放器上方 0–16 px 處。</p>
<figure class="flow-figure">
<ol class="flow-steps">
<li><b>執行真實宿主</b><span>在模擬器中開啟 ChatGPT 手機網頁版</span></li>
<li><b>修正一個原因</b><span>部署、重新整理應用程式的工具，再看一次</span></li>
<li><b>模仿宿主</b><span>從仿真環境的 iframe 送出宿主的訊息</span></li>
<li><b>拿掉修正</b><span>少了這項修正，新測試就必須失敗</span></li>
</ol>
<figcaption>手機上的發現如何變成本機測試。</figcaption>
</figure>
<div class="table-scroll" role="region" aria-label="逐一拿掉修正後的仿真測試" tabindex="0">
<table>
<caption>逐一拿掉各項修正的結果（2026年10月7日）</caption>
<thead><tr><th scope="col">拿掉的修正</th><th scope="col">失敗的檢查</th></tr></thead>
<tbody>
<tr><th scope="row">取邊距或 96 px，不再兩者相加</th><td>輸入框測試：「播放器緊貼輸入框」</td></tr>
<tr><th scope="row">上下文變化時重新定位卡片</th><td>輸入框測試：「卡片不遮住播放器」</td></tr>
<tr><th scope="row">重建的面板讀取目前版本</th><td>重建測試：復原按鈕一直無法使用</td></tr>
<tr><th scope="row">區域尺寸改變時重新定位卡片</th><td>提示測試：「卡片停靠在播放器上」</td></tr>
</tbody>
</table>
</div>
<p>這三個測試跑一次要 59.9 秒。同一天又加了第四個測試，檢查手機編輯器的單列標頭和播放器；我們的貢獻者規則現在要求，只要改動全螢幕版面、邊距、卡片或結果處理，就必須跑手機測試。這次手機實測還發現：對於「Change this note to C」（把這個音改成 C），ChatGPT 把選取的 B4 改成了低七度的 C4；我們在工具描述中加入「取最近八度」的規則後，用真實模型跑的本機測試從 3 次成功 2 次提升到 6 次全部成功。</p>

<h2 id="not-verified">還有哪些沒驗證</h2>
<p>我們的手機測試都是在 iOS 模擬器和 ChatGPT 手機網頁版上進行：沒有用實機，也沒有測原生 App。尚待釐清的有：</p>
<ul>
<li>ChatGPT 實際送出的邊距數值與時機；我們的數值是估算的，仿真環境沿用了這些估算；</li>
<li>Safari 展開的工具列，會擋住一半的輸入框和播放器的下緣；</li>
<li>鍵盤收起後，Safari 留下約 156 點的頁面捲動，跨來源的面板無法把它復原；</li>
<li>在 ChatGPT 還沒回答完時開啟全螢幕，會出現一條最高約 777 點的不透明色帶；</li>
<li>ChatGPT 的底部回覆面板，以及它自己銷毀 iframe 的行為，仿真環境都沒有模仿；</li>
<li>電腦上的 ChatGPT：全螢幕是一個側邊面板，輸入框在面板之外；我們沒有讀取它的邊距。</li>
</ul>

<h2 id="mcp-app-phone-checklist">手機版 MCP App 檢查清單</h2>
<ul>
<li>上下兩個邊距都要留空；如果你還為輸入框保留了備用空間，取兩者中較大的那個，絕對不要相加。</li>
<li>合併每一則 <code>host-context-changed</code> 通知，並重新定位所有用腳本定位的元素，每個影格最多一次。</li>
<li>監看你自己的容器，留意會移動它們的提示。</li>
<li>預期宿主會用舊的工具結果重建畫面；載入時比較結果的版本和伺服器上的版本。</li>
<li>讓本機測試宿主使用在真實宿主上量到的邊距。</li>
<li>依 OpenAI 文件的建議，把 UI 資源 URI 當作快取鍵；發布新的 URI 後，在 ChatGPT 中重新整理該應用程式的工具（在外掛程式設定中依序點選 Manage app、Refresh tools；2026年10月7日確認）。</li>
<li>把在真實宿主上的每個發現都變成一個仿真測試，並確保少了修正時它會失敗。</li>
</ul>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx">SEP-1865：MCP Apps——MCP 的互動式使用者介面（穩定版，2026-01-26）</a> — Model Context Protocol</li>
<li><a href="https://developers.openai.com/plugins/build/chatgpt-ui">為你的 MCP 伺服器加入 UI</a> — OpenAI</li>
<li><a href="https://developers.openai.com/plugins/concepts/ui-guidelines">UI 設計指南</a> — OpenAI</li>
<li><a href="https://developers.openai.com/plugins/reference">參考文件</a>（<code>window.openai</code> 元件橋接介面）— OpenAI</li>
<li><a href="https://sunpeak.ai/docs/mcp-testing">MCP 測試框架</a> — sunpeak</li>
</ol>
</section><h2>常見問題</h2><h3>在手機上，ChatGPT 會把輸入框算進 safeAreaInsets 嗎？</h3><p>我們實測是會的。在 ChatGPT 手機網頁版上（iPhone 17 模擬器，2026年10月7日），底部邊距約 127 px，涵蓋了輸入框；頂部邊距約 47 px，涵蓋了標題列。MCP Apps 規範沒有規定邊距包含什麼，所以把回報的區域留空就好，不要在上面再加自己的餘量。</p><h3>全螢幕的應用程式開啟期間，ChatGPT 會改變邊距嗎？</h3><p>我們實測會：草稿變成兩行，或出現 ChatGPT 的 Thinking 狀態列，都會讓底部邊緣移動。每次收到 <code>ui/notifications/host-context-changed</code> 都要套用新的邊距，把通知中帶的欄位合併進現有的值，並重新定位所有用腳本定位的元素。</p><h3>為什麼在手機上切換到全螢幕後，MCP App 顯示的是舊資料？</h3><p>手機上的 ChatGPT 每次進入或離開全螢幕，都會用對話中最初的工具結果建立新的面板，所以我們的面板顯示的是後續編輯之前的樂譜。畫面載入時，把結果中的版本和伺服器上的版本比較一下；如果結果落後了，就讀取一次目前的狀態。</p><h3>不用手機，可以測試 ChatGPT 在手機上的版面嗎？</h3><p>可以測一部分。我們用 Playwright 在 sunpeak 仿製的手機版 ChatGPT 介面中重現手機上看到的情況：邊距變化、面板重建、從對話發起的編輯；拿掉對應的修正，每個測試都會失敗。ChatGPT 實際送出的數值、Safari 的工具列、鍵盤、ChatGPT 的底部回覆面板和原生 App，仍然需要實機測試。</p>]]></content:encoded>
    </item>
    <item>
      <title>在 Railway 上依 Postgres 佇列自動調整 worker 數量</title>
      <link>https://scorestarling.com/zh-hant/blog/autoscale-workers-postgres-queue</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/autoscale-workers-postgres-queue</guid>
      <pubDate>Wed, 07 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>技術</category>
      <description>我們把轉譜交給 1–4 個 Railway worker，共用一張 Postgres 任務資料表：任務認領、120 秒租約、依佇列深度擴展，以及一個 API bug。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>2026年10月7日，我們把 ScoreStarling 原本唯一的 Railway 服務一分為二：網站只處理請求，1 到 4 個 worker 複本負責轉譜。佇列仍是 PostgreSQL 資料庫中的一張資料表：worker 在交易層級 advisory lock 的保護下認領最早的任務，每 15 秒回報一次；任何任務沉寂兩分鐘，就會被另一個 worker 收回。Railway 不會自己增加複本，所以由擔任 leader 的 worker 依佇列狀況，透過 Railway API 設定複本數；不過第一版查詢了一個 API 中根本不存在的欄位。</p>
</section>

<h2 id="why-separate-workers">為什麼要把轉譜從 Web 伺服器上拆出去？</h2>
<p>因為 Web 伺服器同時也是唯一的轉譜 worker，想擴充只能換更大的容器。10月7日之前，一個 2 vCPU、8 GB 的 Railway 複本既要負責網站、API 和 MCP 端點，又要一個接一個執行所有轉譜。只有持有工作階段層級 advisory lock 的那個執行個體才會處理佇列，所以多加一個複本也不會增加任何轉譜能力。</p>
<p>現在，同一個映像檔透過一項角色設定，以兩個 Railway 服務執行：<code>web</code> 從不接任務，<code>worker</code> 沒有對外網址。一個任務就是一次轉譜（ScoreStarling 能<a href="https://scorestarling.com/zh-hant/how-it-works">把錄音變成樂譜</a>）：在我們抽樣的 10月2日至7日正式環境執行紀錄中，每個任務佔用 worker 17 到 266 秒。</p>
<figure class="diagram">
<svg viewBox="0 0 320 290" aria-hidden="true" focusable="false">
<rect class="d-box" x="80" y="6" width="160" height="46" rx="10"/>
<text class="d-text" x="160" y="26" text-anchor="middle">網站</text>
<text class="d-small" x="160" y="42" text-anchor="middle">網頁、上傳、MCP</text>
<line class="d-line" x1="160" y1="52" x2="160" y2="82"/>
<line class="d-line" x1="155" y1="76" x2="160" y2="82"/>
<line class="d-line" x1="165" y1="76" x2="160" y2="82"/>
<text class="d-small" x="170" y="71">寫入任務</text>
<rect class="d-shade" x="40" y="84" width="240" height="48" rx="10"/>
<text class="d-text" x="160" y="104" text-anchor="middle">PostgreSQL 任務資料表</text>
<text class="d-small" x="160" y="121" text-anchor="middle">即佇列，記錄歸屬與回報</text>
<line class="d-line" x1="52" y1="162" x2="52" y2="134"/>
<line class="d-line" x1="47" y1="140" x2="52" y2="134"/>
<line class="d-line" x1="57" y1="140" x2="52" y2="134"/>
<line class="d-line d-dash" x1="124" y1="162" x2="124" y2="134"/>
<line class="d-line" x1="119" y1="140" x2="124" y2="134"/>
<line class="d-line" x1="129" y1="140" x2="124" y2="134"/>
<line class="d-line d-dash" x1="196" y1="162" x2="196" y2="134"/>
<line class="d-line" x1="191" y1="140" x2="196" y2="134"/>
<line class="d-line" x1="201" y1="140" x2="196" y2="134"/>
<line class="d-line d-dash" x1="268" y1="162" x2="268" y2="134"/>
<line class="d-line" x1="263" y1="140" x2="268" y2="134"/>
<line class="d-line" x1="273" y1="140" x2="268" y2="134"/>
<text class="d-small" x="160" y="152" text-anchor="middle">領取任務</text>
<rect class="d-box d-accent" x="20" y="162" width="64" height="40" rx="8"/>
<rect class="d-box d-dash" x="92" y="162" width="64" height="40" rx="8"/>
<rect class="d-box d-dash" x="164" y="162" width="64" height="40" rx="8"/>
<rect class="d-box d-dash" x="236" y="162" width="64" height="40" rx="8"/>
<text class="d-text" x="52" y="187" text-anchor="middle">worker</text>
<text class="d-text" x="124" y="187" text-anchor="middle">worker</text>
<text class="d-text" x="196" y="187" text-anchor="middle">worker</text>
<text class="d-text" x="268" y="187" text-anchor="middle">worker</text>
<line class="d-accent" x1="52" y1="202" x2="52" y2="244"/>
<line class="d-accent" x1="47" y1="238" x2="52" y2="244"/>
<line class="d-accent" x1="57" y1="238" x2="52" y2="244"/>
<text class="d-small d-mark" x="60" y="226">leader 設定複本數</text>
<text class="d-small" x="300" y="226" text-anchor="end">1 到 4 個複本</text>
<rect class="d-box" x="20" y="246" width="128" height="38" rx="8"/>
<text class="d-text" x="84" y="270" text-anchor="middle">Railway API</text>
<text class="d-small" x="160" y="262">設定複本數，</text>
<text class="d-small" x="160" y="277">不必重新部署</text>
</svg>
<figcaption>網站只負責把任務寫進 PostgreSQL 的一張資料表。1 到 4 個 worker 複本領取任務，每個複本同一時間只處理一個；虛線框的複本只在有任務等待時執行。擔任 leader 的 worker 也會透過 Railway API 設定複本數，Railway 依此啟動或停止複本，不必重新部署。</figcaption>
</figure>

<h2 id="postgres-queue-or-broker">為什麼把佇列放在 Postgres，而不用訊息代理？</h2>
<p>因為任務那一列本身就存著訊息代理（broker）只能指向的東西：歸屬、狀態、點數預扣、價格和檔案。用訊息代理就多了一份需要保持同步的儲存；而且大多數訊息代理只保證至少傳遞一次，而樂團轉譜是對外部服務送出的付費請求，絕不能送出兩次。無論如何，我們都得自己訂出任務歸屬規則。</p>
<p>負載也不大：排隊的任務最多幾十個，每個耗時幾秒到幾分鐘。Railway 的佇列指南說，如果你已經在用 Postgres，拿它做佇列需要的元件比較少，只是吞吐量不如 Redis。pg-boss 這類佇列函式庫則會再多出一張任務資料表。等到每秒幾十個任務時，我們再重新考慮。</p>

<h2 id="postgres-queue-multiple-workers">多個 worker 共用一個 Postgres 佇列，要怎麼避免領到同一個任務？</h2>
<p>每次認領都是一個短交易，整個佇列共用一把鎖。閒置的 worker 先不加鎖檢查有沒有排隊的任務，沒有的話兩秒後再看：</p>
<pre><code>-- One claim, slightly simplified
SELECT 1 FROM jobs WHERE status = 'queued' LIMIT 1;  -- nothing queued: stop here
SELECT pg_advisory_xact_lock(&lt;queue key&gt;);           -- held until COMMIT
SELECT * FROM jobs WHERE status = 'queued' ORDER BY created_at LIMIT 1;
UPDATE jobs SET status = 'running', worker_id = $1, heartbeat_at = $2
 WHERE id = $3;
COMMIT;</code></pre>
<p>第二個 worker 會在鎖上等待，它的下一個查詢要等第一個 worker 提交之後才開始。在 PostgreSQL 預設的 Read Committed 隔離等級下，查詢能看到它開始前已提交的資料，所以這個任務已經顯示為執行中。在我們的測試中，6 個 worker 用各自獨立的連線處理完 40 個排隊任務，每個任務都恰好只被領取一次。</p>
<p>worker 很多時，常見做法是 <code>SELECT … FOR UPDATE SKIP LOCKED</code>，PostgreSQL 文件也建議在類似佇列的資料表上這麼做。我們只用一把鎖，是因為上傳、點數異動和任務復原本來就要用它（在本機的 SQLite 版本中對應的是 <code>BEGIN IMMEDIATE</code>），這樣認領就會和這些操作輪流進行。這段等待時間我們還沒有測量。佇列也不需要專屬的資料庫工作階段：不用 <code>LISTEN</code>（PgBouncer 的 transaction pooling 模式不支援它），也不用我們以前那個唯一的 leader 所持有的工作階段層級鎖。</p>

<h2 id="worker-crash-recovery">worker 停止時，它手上的任務會怎樣？</h2>
<figure class="flow-figure">
<ol class="flow-steps">
<li><b>排隊中</b><span>網站儲存上傳的檔案，並新增一列任務資料。</span></li>
<li><b>已認領</b><span>閒置的 worker 在鎖的保護下領取最早的任務。</span></li>
<li><b>執行中</b><span>worker 每 15 秒回報一次，附上目前進行到的階段。</span></li>
<li><b>已完成</b><span>樂譜儲存完畢，worker 再去找下一個任務。</span></li>
</ol>
<figcaption>一個任務的生命週期。每一步都是修改該任務在 PostgreSQL 中的那一列，網站也是從這一列讀取進度。</figcaption>
</figure>
<p>任務會被復原，付費請求也絕不會送出兩次。被主動停止的 worker（例如部署時）會自己把手上的免費任務放回佇列。當掉的 worker 則只是從此沒了動靜。worker 每 15 秒把目前時間和所處階段寫進任務列；每個 worker 每 30 秒會在同一把鎖下檢查一次，找出 120 秒租約已到期卻沒有回報的執行中任務。免費轉譜會被放回佇列一次，再次中斷就判定失敗。樂團轉譜會從儲存下來的請求繼續；沒有儲存請求的，就等待人工審查。如果某個 worker 只是比較慢，發現自己的任務已經交給別人，它會終止自己的轉譜 process，不寫入任何結果。</p>
<p>只能執行一次的工作（例如控制器）在 leader 上執行：leader 是過去 45 秒內曾向 <code>workers</code> 資料表回報的 worker 中 ID 最小的那個，每個 worker 每 15 秒回報一次。整個過程不持有任何鎖，所以 leader 掛掉後，45 秒內就會有新的 leader 接手。</p>

<h2 id="queue-depth-autoscaling">為什麼依佇列深度、而不是 CPU 來調整 worker 數量？</h2>
<p>因為一個 worker 同一時間只執行一個任務，需要的 worker 數就等於排隊中加上執行中的任務數。忙碌 worker 的 CPU 只能說明它在忙，說明不了在等的是一個任務還是二十個；Railway 的自動擴展指南也把佇列深度列為 worker 的擴展訊號。Railway 會自行把容器擴充到 CPU 和記憶體上限，但複本數會一直維持在你設定的值；指南中的控制器擴展時一步到位，縮減時逐步遞減。</p>
<p>我們的縮減方式不同。API 只接受一個複本數，要停掉哪個複本由 Railway 決定，所以我們只在沒有排隊和執行中的任務、也沒有 worker 在忙時才縮減，而且直接縮到一個。</p>
<div class="table-scroll" role="region" aria-label="worker 控制器如何決策" tabindex="0">
<table>
<caption>控制器的決策規則（2026年10月7日的設定）</caption>
<thead><tr><th scope="col">規則</th><th scope="col" class="num">數值</th><th scope="col">原因</th></tr></thead>
<tbody>
<tr><th scope="row">檢查佇列的間隔</th><td class="num">30 秒</td><td>只有 leader 會檢查，所以每個決定只做一次。</td></tr>
<tr><th scope="row">保留的 worker 數與上限</th><td class="num">1 到 4 個</td><td>留一個隨時接下一個任務；上限用來控制帳單。</td></tr>
<tr><th scope="row">已有 worker 看似閒置時，任務要等多久才申請新 worker</th><td class="num">30 秒</td><td>閒置的 worker 通常 2 秒內就會領走任務；沒有閒置的 worker 時，下一次檢查就會申請。</td></tr>
<tr><th scope="row">兩次擴展之間的間隔</th><td class="num">90 秒</td><td>給新 worker 留出啟動時間，再進行下一次申請。</td></tr>
<tr><th scope="row">等待新 worker 出現的時間</th><td class="num">300 秒</td><td>在此之前，控制器不會再次申請。</td></tr>
<tr><th scope="row">縮回一個之前的閒置時間</th><td class="num">600 秒</td><td>既沒有任務、也沒有忙碌的 worker 時，Railway 停掉的複本必然是閒置的。</td></tr>
</tbody>
</table>
</div>
<p>有任務在排隊時，控制器會依每個排隊中或執行中的任務各一個 worker 來申請，至少比現有的多一個，最多四個。</p>

<h2 id="railway-api-failure">正式環境出了什麼問題？</h2>
<p>問題出在控制器第一次呼叫 Railway 的真實 API；我們的測試用的是一個對任何查詢都照單全收的測試替身。啟用控制器的變更合併五分鐘後，每次檢查佇列都失敗了。worker 照常領取任務，但日誌和 Sentry 裡只有 <code>RailwayError</code>：我們的日誌不記錄例外訊息。</p>
<p>不帶權杖送出同一個查詢，就能重現。Railway 先依 schema 驗證查詢，回傳了 HTTP 400 和 <code>GRAPHQL_VALIDATION_FAILED</code>。我們的服務依區域部署，所以寫入時設定的是 <code>multiRegionConfig</code>，更新操作的輸入型別接受這個欄位；但讀取時卻向服務執行個體查詢這個欄位，而服務執行個體根本沒有它。修正後改從環境設定中讀取複本數，我們的 Railway 設定檔和 <code>railway scale</code> 改的也是這份設定。</p>
<pre><code># Before: rejected with GRAPHQL_VALIDATION_FAILED
query ($service: String!, $environment: String!) {
  serviceInstance(serviceId: $service, environmentId: $environment) {
    numReplicas multiRegionConfig
  }
}

# After: the count is at services.&lt;service id&gt;.deploy.multiRegionConfig
query ($environment: String!) {
  environment(id: $environment) { config(decryptVariables: false) }
}

# The write, unchanged; input: {"multiRegionConfig": {"&lt;region&gt;": {"numReplicas": 2}}}
mutation ($service: String!, $environment: String!, $input: ServiceInstanceUpdateInput!) {
  serviceInstanceUpdate(serviceId: $service, environmentId: $environment, input: $input)
}</code></pre>
<p>現在日誌會記錄 Railway 自己回傳的錯誤訊息（其中從不包含權杖），因為權杖被拒和查詢被拒回傳的都是 HTTP 400。測試替身現在也會拒絕舊的查詢。我們還加了一項新檢查：用佔位 ID、不帶權杖，把兩個操作送到 Railway 實際運作的 schema。GraphQL 伺服器會先驗證再執行，所以欄位寫錯會在驗證階段失敗，而有效的查詢會一路走到 Railway 回傳「Not Authorized」。</p>
<figure class="timeline-figure">
<ol class="timeline">
<li><time datetime="2026-10-06">10月6日</time><p>佇列可容納的等待任務數提高為原本的數倍，但仍然只有一個 worker 在處理。</p></li>
<li><time datetime="2026-10-07T04:22Z">10月7日 04:22</time><p>合併：worker 共用佇列，網站旁新增一個 worker 服務。測試中，兩個上傳同時執行。</p></li>
<li><time datetime="2026-10-07T04:48Z">04:48</time><p>合併：網站不再接任務，並啟用控制器。</p></li>
<li><time datetime="2026-10-07T04:53Z">04:53</time><p>每次檢查佇列都以 <code>RailwayError</code> 失敗。任務照常執行。</p></li>
<li><time datetime="2026-10-07T05:07Z">05:07</time><p>合併：改從環境設定中讀取複本數。</p></li>
<li><time datetime="2026-10-07T05:12Z">05:12</time><p>第一次從 Railway 讀到複本數：一個 worker，狀態穩定。</p></li>
<li><time datetime="2026-10-07T05:17Z">05:17</time><p>同時來了兩個任務。控制器申請第二個 worker，17 秒後它就啟動了。</p></li>
<li><time datetime="2026-10-07T05:28Z">05:28</time><p>閒置 602 秒後縮回一個。直到 06:28 都沒有錯誤。</p></li>
</ol>
<figcaption>上線過程，摘自我們的驗證紀錄。時間皆為 UTC。</figcaption>
</figure>
<p>同一天，leader 開始儲存每次檢查的結果，供網站的健康狀態頁面使用：Railway 拒絕權杖，或兩分鐘內沒有任何檢查，狀態頁面都會發出警告。網站本身的健康檢查不再涵蓋 worker，所以 worker 故障現在會反映在每五分鐘一次的服務檢查中；某個服務連續兩次狀態不佳，Sentry 就會寄電子郵件通知我們。</p>

<h2 id="not-verified-yet">還沒驗證的部分，以及成本</h2>
<ul>
<li>worker 上跑過的只有免費轉譜；樂團轉譜在 worker 上的執行，以及 worker 遺失後的復原，都還沒測過。</li>
<li>我們的測試帳號最多只能同時處理兩份樂譜，而且我們跳過了原本規劃的幾天 dry-run 日誌觀察，所以控制器只經歷過一次小規模的突發流量和一小時的閒置。三到四個 worker、90 秒的擴展間隔、停掉忙碌的複本，這些都還沒在正式環境中發生過。</li>
<li>正式環境沒有記錄任務被領取的時間，所以我們還說不出排隊等待縮短了多少。在擴展測試中，新 worker 啟動後無事可做：第一個 worker 已經領走了那個等待中的任務。</li>
<li>閒置十分鐘後 worker 池會縮回一個，所以突發的任務最多要等 30 秒才輪到下一次檢查，接著還要等新 worker 啟動：在我們唯一一次的測試中是 17 秒。</li>
<li>達到上限時，同時執行四個任務，其餘的排隊等待。</li>
<li>每次部署都會重新啟動所有 worker，執行中的免費轉譜會被放回佇列一次；CI 每次套用我們的 Railway 設定時，也會把複本數重設為一，直到下一次檢查。</li>
</ul>
<p>Railway 按分鐘計費，每 vCPU 每月 US$20，每 GB 記憶體每月 US$10（2026年10月7日確認）。我們估算，一個 worker 以 2 vCPU、1.5 GB 轉譜時每小時約 US$0.08，等待時則少得多。Railway 上的硬性用量上限一旦達到，所有工作負載都會停止運作，所以調高 worker 上限時，也要同時調高這類用量上限。</p>

<h2 id="worker-autoscaling-checklist">如果你也想自己做</h2>
<ul>
<li>除非需要訊息代理的路由能力或吞吐量，否則就把佇列和任務狀態放在一起。</li>
<li>在一個短交易中完成認領；worker 多時用 <code>SKIP LOCKED</code>。</li>
<li>佇列不要依賴工作階段狀態：不用 <code>LISTEN</code>，也不用工作階段層級的鎖。</li>
<li>為每個執行中的任務指定歸屬和心跳；任務被收回的 worker 要直接停止，不寫入任何結果。付費的外部請求絕不重送，而是憑儲存的 ID 繼續。</li>
<li>依排隊中加執行中的任務數調整規模；如果要停掉哪個複本由平台決定，就只在沒有 worker 忙碌時縮減。</li>
<li>用平台實際運作的 schema 測試你的 API 呼叫；確認錯誤訊息不含機密後，再把它們寫進日誌。</li>
<li>給控制器一個回報狀態的地方。</li>
</ul>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS">13.3. 明確鎖定：13.3.5 Advisory Locks，PostgreSQL 18 說明文件</a> — PostgreSQL 全球開發小組</li>
<li><a href="https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS">9.28. 系統管理函式：9.28.10 Advisory Lock 函式，PostgreSQL 18 說明文件</a> — PostgreSQL 全球開發小組</li>
<li><a href="https://www.postgresql.org/docs/current/sql-select.html#SQL-FOR-UPDATE-SHARE">SELECT：鎖定子句，PostgreSQL 18 說明文件</a> — PostgreSQL 全球開發小組</li>
<li><a href="https://www.postgresql.org/docs/current/transaction-iso.html#XACT-READ-COMMITTED">13.2. 交易隔離：13.2.1 Read Committed 隔離等級，PostgreSQL 18 說明文件</a> — PostgreSQL 全球開發小組</li>
<li><a href="https://www.pgbouncer.org/features.html">PgBouncer 功能</a> — PgBouncer</li>
<li><a href="https://docs.railway.com/guides/autoscale-horizontally">依負載自動水平擴展服務</a> — Railway</li>
<li><a href="https://docs.railway.com/guides/cron-workers-queues">排程工作、背景 worker 與佇列，該選哪一個</a> — Railway</li>
<li><a href="https://docs.railway.com/pricing/plans">價格方案</a> — Railway</li>
<li><a href="https://docs.railway.com/pricing/cost-control">成本控管</a> — Railway</li>
<li><a href="https://spec.graphql.org/October2021/#sec-Validating-Requests">GraphQL 規範（2021年10月版）：6.1.1 驗證請求</a> — GraphQL 基金會</li>
</ol>
</section><h2>常見問題</h2><h3>Railway 會自動水平擴展複本嗎？</h3><p>截至 2026年10月7日不會。Railway 會在上限內給容器更多 CPU 和記憶體，但服務的複本數會一直維持在你設定的值。它的自動擴展指南要你自己執行一個控制器，讀取負載訊號，再呼叫 API 的 <code>serviceInstanceUpdate</code>。</p><h3>Postgres 任務佇列該用 SKIP LOCKED 還是 advisory lock？</h3><p><code>FOR UPDATE SKIP LOCKED</code> 能讓多個 worker 同時認領不同的資料列，PostgreSQL 文件也建議在類似佇列的資料表上使用它。用一把交易層級的 advisory lock，則會讓認領輪流進行；worker 不多時這樣比較簡單，尤其是這把鎖本來就在保護你的其他寫入時。</p><h3>為什麼 Railway API 對 multiRegionConfig 回傳 GRAPHQL_VALIDATION_FAILED？</h3><p>我們遇到的原因是：查詢要求服務執行個體回傳 <code>multiRegionConfig</code>，但這個欄位只有更新操作的輸入型別才有。應該從 <code>environment(id) { config }</code> 中的 <code>services.&lt;service id&gt;.deploy.multiRegionConfig</code> 讀取複本數，再用 <code>serviceInstanceUpdate</code> 寫入。</p><h3>移除 worker 複本時，正在執行的任務會怎樣？</h3><p>我們的控制器只在沒有 worker 忙碌時才移除複本。如果忙碌的 worker 仍被停掉（例如部署時），它會把免費轉譜任務放回佇列一次；如果它直接消失，另一個 worker 會在 120 秒沒收到回報後把任務收回。付費請求絕不會重送。</p><h3>在 Railway 上多一個 worker 要花多少錢？</h3><p>Railway 按分鐘計費，每 vCPU 每月 US$20，每 GB 記憶體每月 US$10（2026年10月7日確認）。我們估算，一個 worker 以 2 vCPU、1.5 GB 轉譜時每小時約 US$0.08，等待時則少得多。我們最多只開四個 worker。</p>]]></content:encoded>
    </item>
    <item>
      <title>ChatGPT 和 Claude 能把錄音轉成樂譜嗎？</title>
      <link>https://scorestarling.com/zh-hant/blog/chatgpt-claude-sheet-music</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/chatgpt-claude-sheet-music</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>技術</category>
      <description>單靠它們自己不行；透過 MCP 串接扒譜工具後，Claude 和 ChatGPT 就能把錄音變成可編輯的樂譜。本文介紹設定方法、一次完整的範例對話和目前的限制。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>單靠它們自己不行。AI 聊天助理可以聊音樂，也能用文字寫出音符，但沒辦法根據你自己的錄音給出一份可靠、可編輯的樂譜；我們在 2026年10月3日查閱了兩家公司的說明文件，都沒有提到可以把音訊轉成樂譜。透過 MCP 串接扒譜工具後，兩者就都能做到：工具負責辨識音符、排版樂譜，助理負責把你的要求變成工具呼叫。ScoreStarling 就是這樣和 Claude、ChatGPT 搭配運作的：它正在等待兩邊應用程式目錄的審核，不過你現在就能用網址新增。</p>
</section>

<h2 id="what-happens-if-you-ask">直接讓 ChatGPT 或 Claude 扒譜會怎樣？</h2>
<p>你得到的是對音樂的文字描述，或者助理自己寫出的音符，而不是你這個檔案的轉譜結果。Claude 說明中心列出的可上傳檔案是文件（PDF、DOCX、CSV、TXT 等）和圖片（JPEG、PNG、GIF、WebP），裡面沒有任何音訊格式。OpenAI 的音訊檔案轉錄指南講的是把語音錄音轉成逐字稿，沒有提到音樂或記譜。以上兩項均於 2026年10月3日查閱。</p>
<p>助理能做的，是用文字寫音樂。例如 ABC 記譜法就是純文字，軟體可以把它畫成五線譜；MCP Apps 的官方範例 Sheet Music Server 會把助理寫出的 ABC 渲染成可以播放的樂譜。記錄靈感時這很有用，但它不是轉譜：沒有任何東西測量過你錄音裡的音高和節奏，所以每個音你都得靠耳朵核對。</p>
<p>一份能用的轉譜，背後是一連串工作：從音訊裡找出每個音的音高、起點和時值；把音符對齊到拍子和拍號上；決定調號、選定變音記號的寫法、放好休止符、分開聲部；最後排版成譜，存成打譜軟體能開啟的格式。每一步都有專門的軟體。第一步由 Spotify 開源的 Basic Pitch 完成，我們在自己的伺服器上用它處理單一樂器或人聲，它的作者說它一次只處理一種樂器時效果最好。中間幾步由 MuseScore Studio 4.7.5 的 MIDI 匯入功能替我們完成，最後由 Verovio 用 Leipzig 音樂字型排版。</p>

<h2 id="what-mcp-changes">串接 MCP 工具後有什麼不同？</h2>
<p>助理可以把音樂交給專門的軟體處理，而你照樣用日常的話提出要求。用官方文件的話來說，模型上下文協定（Model Context Protocol）是「一個將 AI 應用程式連接到外部系統的開源標準」。服務商會架設一個提供工具的 MCP 伺服器，連上它的助理就能在對話中途呼叫這些工具。在 Claude 裡，你自己新增的伺服器叫作自訂連接器；在 ChatGPT 裡，它是你在開發者模式下建立的應用程式。</p>
<p>連接 ScoreStarling 時，瀏覽器會開啟我們的登入頁面。你授權之後，助理只能處理你自己的專案，碰不到別人的。之後的分工如下：</p>
<div class="table-scroll" role="region" aria-label="AI 助理和 ScoreStarling 的工具各做什麼" tabindex="0">
<table>
<caption>連接後的對話中，誰負責什麼</caption>
<thead>
<tr><th scope="col">環節</th><th scope="col">AI 助理</th><th scope="col">ScoreStarling 的工具</th></tr>
</thead>
<tbody>
<tr><th scope="row">你的要求</th><td>理解「第 1 小節最後一個音，高八度」指的是什麼，並選擇工具</td><td>回傳樂譜的音符、小節和版本，讓要求能對應到具體的音符</td></tr>
<tr><th scope="row">錄音</th><td>轉交你的附件（ChatGPT），或從程式碼沙箱上傳（Claude）</td><td>簽發一次性上傳連結，10 分鐘後失效</td></tr>
<tr><th scope="row">轉譜</th><td>查詢任務狀態，而不是重新上傳</td><td>辨識音符（單一樂器或人聲用 Basic Pitch），寫成樂譜並用 Verovio 排版</td></tr>
<tr><th scope="row">修改</th><td>把你的話變成一次精確的編輯，自己的建議則以預覽形式給出</td><td>執行編輯，支援復原和儲存版本；產生附音訊的預覽</td></tr>
<tr><th scope="row">匯出</th><td>在對話裡把檔案交給你，或引導你到面板的 Download 選單</td><td>產生 PDF、MusicXML、MIDI、MP3 或 WAV；連結一小時後失效</td></tr>
</tbody>
</table>
</div>
<p>這些工具和<a href="https://scorestarling.com/app">網頁工作區</a>操作的是同一份已儲存的樂譜，所以在對話裡做的修改，開啟工作區就能看到。</p>

<h2 id="a-session-step-by-step">從錄音到 PDF，一次完整的對話是什麼樣子？</h2>
<p>幾句簡單的話，就能從錄音一路走到最終檔案：轉譜、修改、預覽、匯出，中間隨時試聽。下面的步驟用的是我們<a href="https://scorestarling.com/zh-hant/mcp#ask">連線指南</a>裡的範例提示詞。這些步驟大多已在 ChatGPT 的真實對話中實際跑過，只是沒有在同一段對話裡一次走完：2026年10月5日轉了一段附件錄音；10月6日測試了從連結轉譜、樂譜面板、播放、預覽、套用、復原和下載。在 Claude 中，10月6日有一份直接用音符寫成的樂譜在面板裡開啟並成功播放；從 Claude 上傳錄音仍在測試中。</p>
<ol>
<li>附上錄音，然後說：「用 ScoreStarling 把這段語音備忘錄轉成樂譜，速度大約 84 BPM。」如果知道速度，就告訴它。沒給速度時，ScoreStarling 會根據錄音估算；如果演奏帶有搖擺或彈性速度，估出來的可能是一個相關但不同的速度。</li>
<li>檔案在兩款應用程式裡的傳遞方式不同。ChatGPT 會把附件直接交給轉譜工具。Claude 會在程式碼沙箱裡讀取檔案，向 ScoreStarling 申請一次性上傳連結，再把檔案傳過去，所以沙箱需要網路存取權限。如果你用的應用程式無法上傳，助理可以開啟 ScoreStarling 的上傳面板，你也可以在網頁工作區上傳這段錄音。</li>
<li>我們的伺服器會依你給的速度或偵測到的速度轉譜。人聲或單一樂器先按 4/4 拍記譜，之後可以修改；鋼琴獨奏還會自動判斷拍號和小節線。如果你的應用程式能顯示面板，助理會在對話中的面板開啟樂譜。同一個專案也在工作區裡：工作區能把轉譜結果和你的錄音同步播放，支援交叉淡化和重複播放，方便你聽出兩者哪裡對不上。</li>
<li>聽到問題就改：「我的 ScoreStarling 樂譜裡，第 1 小節最後一個音應該高八度。」助理會讀取樂譜找到這個音，然後做出精確的修改。你明確說出的修改會立即生效，也可以復原。</li>
<li>改動較大時，先試再定：「把我的 ScoreStarling 樂譜升高一個全音，先讓我聽聽預覽。」工具會產生移調後的樂譜和音訊作為預覽。在你接受之前，樂譜不會變動；你也可以直接捨棄這個預覽。</li>
<li>匯出：「把我的 ScoreStarling 樂譜匯出成 PDF 和 MusicXML。」你可以在對話裡或面板的 Download 選單拿到這兩個檔案，連結一小時後失效。PDF 可以直接列印，樂譜解鎖前頁尾會有一行小字；MusicXML 可以在 MuseScore、Dorico、Sibelius 或 Finale 裡開啟繼續編輯。匯出 MusicXML 需要先解鎖樂譜，一次性花費 15 點，助理會在扣點之前先徵求你的同意。</li>
</ol>
<p>指南裡還有兩句值得記下來。「用長笛播放我的 ScoreStarling 旋律」會更換播放和匯出音訊時使用的 General MIDI 音色。「我的 ScoreStarling 帳號裡還有任務在執行嗎？」能讓你在開始新任務前查看進度，因為免費帳號同時只能執行一個任務。</p>

<h2 id="connect-claude">如何在 Claude 中連接 ScoreStarling？</h2>
<p>把 ScoreStarling 新增為自訂連接器（custom connector）。Anthropic 說明中心表示，免費版、Pro、Max、Team 和 Enterprise 方案都能使用自訂連接器，免費版帳號限一個（2026年10月3日查閱）。</p>
<ol>
<li>開啟 Customize → Connectors，點選 + Add，再點選 Add custom connector。名稱填 ScoreStarling，網址填 <code>https://mcp.scorestarling.com/mcp</code>。</li>
<li>在 Authentication 下選擇 Sign in now，在 OAuth client 下選擇 Register automatically。Claude 推薦的是使用它公開的身分（published identity），但這個選項需要用戶端 ID 中繼資料文件（client ID metadata documents），而我們的登入伺服器暫時還不支援。</li>
<li>登入 ScoreStarling。在對話中點選輸入框裡的 +，開啟 Connectors，然後啟用 ScoreStarling。</li>
</ol>
<p>在 Team 和 Enterprise 方案中，需要先由擁有者在 Organization settings → Connectors 裡新增連接器，成員再點選 Connect。要轉譜你附上的錄音，必須允許 Claude 的程式碼沙箱存取 ScoreStarling，<a href="https://scorestarling.com/zh-hant/mcp#claude-upload">指南裡有這項設定的說明</a>。從 Claude 上傳仍在測試中，所以上傳面板和工作區仍是備用方案。<a href="https://scorestarling.com/zh-hant/mcp#claude">Claude 設定步驟</a>裡還有一個按鈕，可以幫你自動填好表單；由於 Anthropic 沒有為這個捷徑提供說明文件，旁邊也列出了手動步驟。</p>
<p>在 Claude Code 裡只需要一行指令，然後在工作階段中輸入 <code>/mcp</code> 登入：</p>
<pre><code>claude mcp add --transport http --scope user scorestarling https://mcp.scorestarling.com/mcp</code></pre>

<h2 id="connect-chatgpt">如何在 ChatGPT 中連接 ScoreStarling？</h2>
<p>使用開發者模式。OpenAI 向網頁版的 Plus、Pro、Business、Enterprise 和 Education 帳號提供這項功能（2026年10月3日查閱）。下面的選單名稱取自 ChatGPT 英文介面，中文介面中的名稱可能不同。</p>
<ol>
<li>在 Settings → Security and login 中開啟 Developer mode。在 Business、Enterprise 和 Edu 工作空間中，可能需要管理員先允許。</li>
<li>開啟 Plugins，點選 +，把應用程式命名為 ScoreStarling，並貼上 <code>https://mcp.scorestarling.com/mcp</code>。驗證方式選擇 OAuth，使用動態用戶端註冊（dynamic client registration）；不需要 API 金鑰。</li>
<li>登入 ScoreStarling。應用程式會出現在 Drafts 下；在輸入框的 Developer mode 工具裡點選它即可。</li>
</ol>
<p>OpenAI 的指南把開發者模式標為高風險功能，並說明寫入操作預設需要確認，所以工具修改內容之前，ChatGPT 會先徵求你的同意。ScoreStarling 正在等待 ChatGPT 應用程式目錄的審核，所以目前在目錄裡還搜尋不到。如果找不到 Developer mode 或 + 按鈕，<a href="https://scorestarling.com/zh-hant/mcp#chatgpt">ChatGPT 設定步驟</a>裡說明了該檢查什麼。</p>

<h2 id="what-doesnt-work-yet">目前還有哪些做不到？</h2>
<p>有些功能還在測試，有些不在服務範圍內。產品限制於 2026年10月7日確認，用戶端選單於 10月3日查閱。</p>
<ul>
<li>ScoreStarling 可以免費開始使用。單一樂器或人聲、鋼琴獨奏、匯入、編輯，以及下載 PDF、MP3 和 WAV 都免費；2026年內每個帳號每月可免費獲得 100 點，2027年1月起改為 50 點。樂團轉譜要用點數，解鎖樂譜的 MusicXML 和 MIDI 需要 15 點（詳見<a href="https://scorestarling.com/zh-hant/pricing">價格</a>）。在對話中，ScoreStarling 只會使用你已有的點數，不會向你販售任何東西。免費帳號同時只能處理一份樂譜，Plus 帳號可以同時處理五份。</li>
<li>ChatGPT 和 Claude 的目錄裡都還沒有它：我們在 2026年10月6日向兩邊提交了申請，目前正在等待審核。上架之前，在 ChatGPT 中需要使用開發者模式。</li>
<li>在我們的測試中，對話裡的樂譜面板在 ChatGPT 和 Claude 中都能開啟，ChatGPT 也已經成功把附件錄音傳給 ScoreStarling；從 Claude 和其他用戶端上傳仍在測試中。每一步都可以改用網頁工作區完成。</li>
<li>上傳檔案最大 100 MiB，無論選哪一種，都只處理錄音的前 5 分鐘。你可以上傳音訊或影片、貼上連結，或者直接在瀏覽器裡錄音。請依錄音裡的內容選擇對應的選項，詳見<a href="https://scorestarling.com/zh-hant/how-it-works#engines">選項說明</a>。</li>
<li>不指定速度時會自動偵測。人聲或單一樂器先按 4/4 拍記譜，之後可以修改；鋼琴獨奏會自動判斷 4/4、3/4、6/8 或 12/8 拍，以及小節線和弱起小節；樂團樂譜的拍號依演奏判斷，或在開始前手動選擇。所有估算結果都需要檢查。TAB 譜不記推弦、滑音和搥弦；匯入的樂譜和簡譜檢視另有各自的<a href="https://scorestarling.com/zh-hant/how-it-works#specs">限制</a>。</li>
<li>遠端 MCP 現在也能辨識樂譜 PDF 和圖片、讀取樂譜檔案，並能根據 ABC 或 MusicXML 文字建立樂譜。這類記譜文字可以由助理自己寫；作曲由助理完成。ScoreStarling 的外掛程式（含兩個技能）也在等待目錄審核。<a href="https://scorestarling.com/zh-hant/mcp#webmcp">WebMCP Beta</a> 則是另一條途徑，供在瀏覽器中開啟工作區的 AI 代理使用。</li>
<li>它不會產生新的錄音。匯出的 MP3 和 WAV 是用 General MIDI 音色把樂譜合成出來的音訊，不是從你的錄音裡分離出的分軌。</li>
<li>每份轉譜結果都只是初稿。我們 10月2日的比較測試測的是吉他錄音和一段樂團片段，沒有測人聲或鋼琴獨奏，結果已發表在《<a href="https://scorestarling.com/zh-hant/blog/ai-music-transcription-accuracy">AI 扒譜到底有多準？我們實測了一下</a>》一文中。分享樂譜之前，請先聽一遍並修正。</li>
</ul>

<h2 id="other-ways">除了 ScoreStarling，還有哪些辦法？</h2>
<p>也有其他服務把音樂工具串接到 AI 助理。以下內容依據各家自己的頁面，於 2026年10月3日查閱。</p>
<ul>
<li>Mirelo 在 <code>https://mcp.mirelo.ai/mcp</code> 提供託管的 MCP 伺服器。用 Mirelo 帳號登入即可，不需要 API 金鑰；它的指南涵蓋 Claude、開發者模式下的 ChatGPT、Cursor、Claude Code 和 Codex。它在 2026年7月30日的發布公告中說，Audio-to-MIDI 可用於現有錄音；其外掛程式儲存庫中的 Audio-to-MIDI 技能會回傳可下載的 MIDI 和 MusicXML，不指定速度時會自動偵測。它的其他工具大多用來產生和編輯音效。Mirelo 同時也是 ScoreStarling 樂團轉譜的合作夥伴，所以我們對它並不中立。</li>
<li>Melogen 的 MCP 伺服器適用於開發者模式下的 ChatGPT，透過 OAuth 登入；它能把 PDF、PNG 或 JPG 格式的印刷樂譜辨識成 MIDI 和 MusicXML，還能分析歌曲的速度、調性和結構。它公開的外掛程式裡沒有把錄音轉成樂譜的工具。</li>
<li>如果你更想全部在自己的電腦上執行，可以試試 audio2score-mcp：這是一個開源的本機伺服器，把 Basic Pitch 和 music21 串接起來，輸出 <code>.mid</code> 和 <code>.musicxml</code> 檔案，並附有 Claude Code 的設定說明。它不需要帳號，最近一次提交是在 2026年8月20日。</li>
<li>不用 AI 助理的話，直接在 ScoreStarling 的網頁工作區上傳錄音，得到的是同樣的樂譜。</li>
</ul>
<p>我們在《<a href="https://scorestarling.com/zh-hant/blog/sheet-music-mcp-servers">樂譜 MCP 伺服器盤點：各自能做什麼</a>》一文中並排比較了其中 26 個，包括操控 MuseScore 的伺服器和用 music21 分析樂譜的伺服器。</p>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://modelcontextprotocol.io/docs/getting-started/intro">What is the Model Context Protocol (MCP)?</a>——Model Context Protocol 文件</li>
<li><a href="https://support.claude.com/en/articles/8241126-upload-files-to-claude">Upload files to Claude</a>——Claude 說明中心</li>
<li><a href="https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp">Get started with custom connectors using remote MCP</a>——Claude 說明中心</li>
<li><a href="https://support.claude.com/en/articles/12111783-create-and-edit-files-with-claude">Create and edit files with Claude</a>——Claude 說明中心</li>
<li><a href="https://developers.openai.com/api/docs/guides/developer-mode">ChatGPT Developer mode</a>——OpenAI</li>
<li><a href="https://developers.openai.com/api/docs/guides/speech-to-text">File transcription</a>——OpenAI API 文件</li>
<li><a href="https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/sheet-music-server">Example: Sheet Music Server</a>——MCP Apps（modelcontextprotocol/ext-apps），GitHub</li>
<li><a href="https://github.com/spotify/basic-pitch">Basic Pitch</a>——Spotify，GitHub</li>
<li><a href="https://mirelo.ai/mcp">Mirelo MCP: sound design for any AI agent</a>——Mirelo</li>
<li><a href="https://mirelo.ai/blog/introducing-mirelo-mcp">Generate Sound Effects in Claude, ChatGPT and Cursor</a>——Mirelo 部落格，2026年7月30日</li>
<li><a href="https://github.com/mirelo-ai/mirelo-plugins">Mirelo plugins for Cursor and Claude</a>——Mirelo，GitHub</li>
<li><a href="https://melogenai.com/mcp">Melogen Music Intelligence for ChatGPT and MCP</a>——Melogen AI</li>
<li><a href="https://github.com/David7ce/audio2score-mcp">audio2score-mcp</a>——David7ce，GitHub</li>
</ol>
</section><h2>常見問題</h2><h3>在 ChatGPT 或 Claude 裡使用樂譜連接器，需要付費方案嗎？</h3><p>ChatGPT 需要：開發者模式只開放給網頁版的 Plus、Pro、Business、Enterprise 和 Education 帳號。Claude 的所有方案都能新增自訂連接器，免費版限一個。以上兩項均於 2026年10月3日查閱。在 ScoreStarling 上，單一樂器或人聲、鋼琴獨奏和編輯都免費；樂團轉譜要用點數，解鎖樂譜的 MusicXML 和 MIDI 也要用點數。2026年內，每個帳號每月可免費獲得 100 點，2027年1月起改為每月 50 點（2026年10月7日確認）。</p><h3>ScoreStarling 已經上架 ChatGPT 或 Claude 的應用程式目錄了嗎？</h3><p>還沒有。我們在 2026年10月6日向兩邊的目錄提交了申請，目前正在等待審核。在此之前，請依<a href="https://scorestarling.com/zh-hant/mcp">連線指南</a>的說明，用網址 <code>https://mcp.scorestarling.com/mcp</code> 新增 ScoreStarling。任何人都可以用 Google 帳號或電子郵件登入。</p><h3>能扒整個樂團的錄音嗎？</h3><p>可以：選擇樂團即可。樂團轉譜以每分鐘 40 點計費，開始前會先顯示價格。樂團錄音由我們的合作夥伴 Mirelo 轉譜，和所有轉譜結果一樣，需要你檢查一遍。單一樂器或人聲、鋼琴獨奏都免費。無論選哪一種，都只處理錄音的前 5 分鐘，上傳檔案最大 100 MiB。</p><h3>AI 助理會不經我同意就改動樂譜嗎？</h3><p>你明確說出的修改，例如「把第 1 小節最後一個音升高八度」，會立即生效，也可以復原。助理自己提出的修改，以及你要求先試聽的修改，都會以預覽的形式出現；在你接受之前，樂譜不會有任何變動。</p><h3>AI 助理能根據我的錄音寫出吉他譜嗎？</h3><p>可以，2026年10月8日起支援。向助理要六線譜（TAB），ScoreStarling 就會依你指定的調音和移調夾位置，為吉他、貝斯或烏克麗麗聲部顯示六線譜：可以放在五線譜下方，也可以只顯示六線譜。助理還能把某個音移到另一條弦上，或者把六線譜以文字形式貼進回答裡。推弦、滑音和搥弦目前不會標出。另有一個在本機執行的開源 MCP 伺服器 fingerstyle-tab-mcp，也能把吉他錄音轉成文字六線譜；我們在《<a href="https://scorestarling.com/zh-hant/blog/sheet-music-mcp-servers">樂譜 MCP 伺服器盤點：各自能做什麼</a>》一文中把它和其他伺服器列在一起。</p>]]></content:encoded>
    </item>
    <item>
      <title>AI 扒譜到底有多準？我們實測了一下</title>
      <link>https://scorestarling.com/zh-hant/blog/ai-music-transcription-accuracy</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/ai-music-transcription-accuracy</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>研究</category>
      <description>AI 扒譜出初稿夠用，要當成品還不行。我們用 12 段公開吉他錄音和一段 60 秒樂團片段實測了 Basic Pitch 與 Mirelo 兩款引擎，用白話講清楚起音 F1，並列出結果和限制。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>出初稿夠用，出成品還不行。2026年10月2日，我們用 12 段公開吉他錄音做了測試：Spotify 開源的 Basic Pitch 起音 F1 達到 0.744，Mirelo 的 Audio-to-MIDI 為 0.471；1.0 意味著每個音符的音高和時間都對，而且沒有一個多餘的音。六段獨奏錄音裡有三段 Mirelo 一個音都沒傳回，但在一段 60 秒的樂團片段上，它以 0.672 比 0.520 領先。音符時長遠不如音符起點可靠；而且這些數字都說明不了寫出來的樂譜好不好讀。</p>
</section>

<h2 id="what-onset-f1-means">起音 F1（onset F1）衡量的是什麼？</h2>
<p>起音 F1 把扒譜結果和參考標註逐個音符比對，漏掉的音和多出來的音同樣扣分。扒出的一個音符，只要音高與某個參考音符相差不超過 50 音分（四分之一音），起點相差不超過 50 毫秒，就算配對成功。每個參考音符只能被配對一次。</p>
<p>配對完會得到兩個比例。精確率是模型寫出的音符中配對成功的比例；召回率是參考音符中被模型找到的比例。F1 是兩者的調和平均數，只要其中一個低，F1 就高不起來。假設一段音樂有 100 個音：一個模型寫了 100 個音，對了 75 個，得分 0.75；另一個模型找到了其中 90 個，卻為此寫了 150 個音，召回率 0.9，精確率 0.6，F1 只有 0.72——多寫的 60 個音拉低了分數。</p>
<p>起止 F1（onset+offset F1）再加一個條件：每個音符的結束點也要落在參考音符時長的 20% 以內，如果 50 毫秒更長，就按 50 毫秒算。Spotify 的 Basic Pitch 論文以起音 F1 為主要指標，因為音符結束點不那麼客觀，論文點出了殘響、延音踏板和標註方式這幾個原因。兩個指標我們都會列出。但兩者都不檢查音符被分給了哪件樂器，也不管調號和節奏該怎麼記。</p>

<h2 id="how-we-tested">我們的測試方法</h2>
<p>我們用的是 GuitarSet 1.1.0（Xi 等，ISMIR 2018）。這個公開資料集收錄了 360 段約 30 秒的吉他片段：六位吉他手彈奏同樣的 30 份旋律譜，涵蓋五種風格，每份先彈一遍伴奏（comp），再在伴奏之上彈一遍獨奏。錄音用的是六路分弦拾音器（hexaphonic pickup），每根弦各有一路獨立訊號，作者因此能把音符標註基本自動化。</p>
<p>我們選了 12 段完整錄音，每位吉他手一段獨奏、一段伴奏，用的是單聲道麥克風音軌，共 433 秒。選哪幾段在執行任何模型之前就定好了：先排除三段已知標註有誤的錄音（<a href="https://github.com/marl/GuitarSet/issues">GuitarSet 的 issue #4 和 #5</a>：一個重複的音符和錯誤的時間），再對每位吉他手的每種演奏方式，取「scorestarling-v1:」加檔名的 SHA-256 雜湊值排序最靠前的檔案。下文的<a href="#per-recording">逐段結果表</a>列出了挑出的 12 段。</p>
<p>評分用的是 <a href="https://mir-eval.readthedocs.io/latest/api/transcription.html">mir_eval</a> 0.8.2，容許誤差採用上文所述的預設值，先逐段計算再取平均，讓每段錄音的權重相同。沒有做任何參數調整：不加時間偏移，不做過濾，也不做對齊平移。Basic Pitch 0.4.0 在一台 Mac 上執行，兩次完整執行的輸出逐位元組一致。Mirelo 的 Audio-to-MIDI API（回報的模型版本為 a2m-1.1）在 2026年10月2日處理了同樣的 12 段錄音，沒有附帶可選的樂器列表；據 <a href="https://mirelo.ai/changelog">Mirelo 的更新紀錄</a>，其 API 自 2026年8月6日起支援這個參數。</p>
<p>以下所有結果都受四點限制：</p>
<ul>
<li>Basic Pitch 的論文把 GuitarSet 和 Slakh 列入了訓練資料，所以兩項測試都對它有利。我們無法確認這 12 段 GuitarSet 錄音是否在它的訓練集裡；樂團片段則取自 Slakh 的測試集。</li>
<li>12 段吉他錄音加一段 60 秒樂團片段，只是診斷性的小樣本，不是基準測試。人聲和真實錄製的樂團演奏都沒有測；鋼琴只在合成音訊上測過，用的也是另一個模型（見<a href="#faq">文末問答</a>）。</li>
<li>吉他錄音在 Mirelo 上只跑了一次，是在 10月2日。它沒傳回任何音符的那三段，我們也沒有換個方式重試，比如告訴它錄音裡是原聲吉他。樂團片段在 10月5日重新提交了一次，傳回的音符完全相同。</li>
<li>這些數字都不評價寫出來的樂譜。像 MV2H 這樣在樂譜層面評分、還會檢查拍號、聲部和時值的指標，我們還沒有測。</li>
</ul>
<p class="note">ScoreStarling 扒單一樂器或人聲用的是 Basic Pitch，扒樂團用的是我們的合作夥伴 Mirelo，所以我們並不是中立的旁觀者。正因如此，我們在這裡把方法、樣本和限制都寫清楚。</p>

<h2 id="basic-pitch-vs-mirelo">Basic Pitch 和 Mirelo，誰扒吉他更準？</h2>
<p>Basic Pitch 勝出：獨奏旋律上遙遙領先，伴奏上小幅領先。</p>
<div class="table-scroll" role="region" aria-label="Basic Pitch 與 Mirelo 在 12 段 GuitarSet 錄音上的起音 F1" tabindex="0">
<table>
<caption>GuitarSet，12 段錄音，宏平均，2026年10月2日</caption>
<thead>
<tr><th scope="col">錄音</th><th scope="col" class="num">Basic Pitch 起音 F1</th><th scope="col" class="num">Mirelo 起音 F1</th><th scope="col" class="num">Basic Pitch 起止 F1</th><th scope="col" class="num">Mirelo 起止 F1</th></tr>
</thead>
<tbody>
<tr><th scope="row">獨奏（6 段）</th><td class="num">0.818</td><td class="num">0.301</td><td class="num">0.654</td><td class="num">0.168</td></tr>
<tr><th scope="row">伴奏（6 段）</th><td class="num">0.669</td><td class="num">0.642</td><td class="num">0.362</td><td class="num">0.280</td></tr>
<tr><th scope="row">全部（12 段）</th><td class="num">0.744</td><td class="num">0.471</td><td class="num">0.508</td><td class="num">0.224</td></tr>
</tbody>
</table>
</div>
<p class="note">2026年10月4日更新：ScoreStarling 的單一樂器選項做了兩處改動——整體持續偏高或偏低的錄音會先校準到 A440，旋律改為一次只寫一個音——之後我們用同樣的 12 段錄音重測了一遍。獨奏得分 0.846，伴奏 0.679，總體 0.763，算上音符結束點則為 0.522。上表保留的仍是 10月2日單獨執行 Basic Pitch 的結果。</p>
<p>六段獨奏錄音中，有三段 Mirelo 一個音符都沒傳回。這三段在它的獨奏平均分裡按 0 計算，因為使用者拿到的就是空結果；我們也沒有重跑。傳回了音符的那幾段，表現有好有壞。第一段 00_BN3-154-E_solo 上，兩個引擎在音符起點上打成平手（Basic Pitch 0.809，Mirelo 0.814），但 Mirelo 的音符結束點偏差更大（起止 F1 為 0.320，Basic Pitch 為 0.524），還把這首 154 BPM 的曲子判成了約 76.9 BPM，只有原速的一半。伴奏方面，六段裡有兩段 Mirelo 明顯更好。12 段全部算上，它的起止 F1 為 0.224。</p>
<div class="table-scroll" id="per-recording" role="region" aria-label="Basic Pitch 與 Mirelo 逐段錄音的起音 F1" tabindex="0">
<table>
<caption>逐段起音 F1，GuitarSet，2026年10月2日</caption>
<thead>
<tr><th scope="col">錄音</th><th scope="col" class="num">Basic Pitch</th><th scope="col" class="num">Mirelo</th></tr>
</thead>
<tbody>
<tr><th scope="row">00_BN3-154-E_solo</th><td class="num">0.809</td><td class="num">0.814</td></tr>
<tr><th scope="row">01_Rock2-85-F_solo</th><td class="num">0.869</td><td class="num">0.326</td></tr>
<tr><th scope="row">02_BN1-147-Gb_solo</th><td class="num">0.860</td><td class="num">0.667</td></tr>
<tr><th scope="row">03_SS1-68-E_solo</th><td class="num">0.782</td><td class="num">0（無音符）</td></tr>
<tr><th scope="row">04_SS3-84-Bb_solo</th><td class="num">0.821</td><td class="num">0（無音符）</td></tr>
<tr><th scope="row">05_SS1-68-E_solo</th><td class="num">0.768</td><td class="num">0（無音符）</td></tr>
<tr><th scope="row">00_BN3-154-E_comp</th><td class="num">0.685</td><td class="num">0.810</td></tr>
<tr><th scope="row">01_SS1-68-E_comp</th><td class="num">0.622</td><td class="num">0.854</td></tr>
<tr><th scope="row">02_SS1-100-C#_comp</th><td class="num">0.567</td><td class="num">0.491</td></tr>
<tr><th scope="row">03_SS3-98-C_comp</th><td class="num">0.731</td><td class="num">0.745</td></tr>
<tr><th scope="row">04_Rock3-117-Bb_comp</th><td class="num">0.683</td><td class="num">0.385</td></tr>
<tr><th scope="row">05_Rock2-85-F_comp</th><td class="num">0.727</td><td class="num">0.564</td></tr>
</tbody>
</table>
</div>
<p>Basic Pitch 的 0.744，與作者在他們自己的 GuitarSet 測試集（72 段錄音）上報告的結果相近：起音 F1 為 0.79，算上結束點為 0.56。兩個引擎的速度也不同。Basic Pitch 在一台 Mac 上用 54 秒處理完了 433 秒的音訊，這並不代表正式上線環境的耗時；第一段錄音它用了 6.1 秒，Mirelo 用了 37 秒。</p>

<h2 id="full-band-recording">換成整支樂團會怎樣？</h2>
<p>混音更難扒，這一回是 Mirelo 領先。我們取了 <a href="https://zenodo.org/records/4599666">Slakh2100</a> 測試集中 Track01881 的前 60 秒。Slakh2100 收錄了 2,100 首歌，都是用取樣式虛擬樂器演奏 MIDI 產生的，所以參考音符分毫不差。這一分鐘裡，鋼弦吉他、爵士吉他、鋼琴、貝斯、顫音琴和鼓都有演奏。把所有有音高的樂器合在一起算、不管音符被分給了哪件樂器，並以兩個引擎各自傳回的 MIDI 檔案評分，Basic Pitch 的起音 F1 為 0.520，Mirelo 為 0.672。</p>
<p>Basic Pitch 只輸出一個合併的聲部，也不寫鼓。Mirelo 為每件樂器各寫一個聲部，所以只有 Mirelo 能按樂器評分，這裡按 General MIDI 的樂器類別分組：</p>
<figure class="bars-figure">
<div class="bars bars-wide" aria-hidden="true">
<p class="bars-title">Mirelo 各樂器的起音 F1 <span>一段 60 秒樂團片段，0 到 1</span></p>
<div class="bar-row"><span>鋼琴</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="82.7%" height="10" rx="3"/></svg><b>0.827</b></div>
<div class="bar-row"><span>鼓</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="69.8%" height="10" rx="3"/></svg><b>0.698</b></div>
<div class="bar-row"><span>吉他</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="68.1%" height="10" rx="3"/></svg><b>0.681</b></div>
<div class="bar-row"><span>貝斯</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="45.5%" height="10" rx="3"/></svg><b>0.455</b></div>
<div class="bar-row"><span>顫音琴</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="13.7%" height="10" rx="3"/></svg><b>0.137</b></div>
</div>
<figcaption>鋼琴和鼓扒得最好；顫音琴大多被漏掉了。參考音符數量見下表。</figcaption>
</figure>
<div class="table-scroll" role="region" aria-label="Mirelo 在 Slakh2100 片段上各樂器的起音 F1" tabindex="0">
<table>
<caption>Mirelo 各樂器類別得分，Slakh2100 Track01881，前 60 秒</caption>
<thead>
<tr><th scope="col">樂器類別（參考音符數）</th><th scope="col" class="num">起音 F1</th></tr>
</thead>
<tbody>
<tr><th scope="row">鋼琴（178）</th><td class="num">0.827</td></tr>
<tr><th scope="row">吉他：鋼弦和爵士（334）</th><td class="num">0.681</td></tr>
<tr><th scope="row">貝斯（127）</th><td class="num">0.455</td></tr>
<tr><th scope="row">顫音琴（64）</th><td class="num">0.137</td></tr>
<tr><th scope="row">鼓點（423）</th><td class="num">0.698</td></tr>
</tbody>
</table>
</div>
<p>要把兩把吉他區分開就更難了。按 10月2日 Mirelo 傳回的音符列表逐聲部評分，鋼弦吉他為 0.364，爵士吉他為 0.148；MIDI 檔案裡的音符比這份列表早約 20 毫秒開始，所以按 MIDI 算出的數字略有不同。Mirelo 還給歌曲裡根本沒有的樂器報了 92 個音：管風琴、合成器鋪底音色（pad）、小提琴和大提琴。鼓點只按時間評分，不管是哪面鼓。這只是一首歌，而且是由 MIDI 產生的，不是實際錄音，所以只能看個大致方向，不能當排名。</p>

<h2 id="which-notes-are-wrong">哪些音最可能扒錯？</h2>
<p>對 Basic Pitch 來說，是它以低力度寫出的那些音。Basic Pitch 把每個音符的 MIDI 力度設為 127 乘以模型在這個音符上的平均活化值，所以力度同時也是一種信心分數。它為 12 段吉他錄音一共寫了 2,715 個音符，按同樣的 50 毫秒時間窗比對，低力度的音大多是錯的：</p>
<figure class="bars-figure">
<div class="bars bars-wide" aria-hidden="true">
<p class="bars-title">Basic Pitch 扒對的音符比例，按力度分組 <span>12 段吉他錄音中的 2,715 個音符</span></p>
<div class="bar-row"><span>低於 50</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="21%" height="10" rx="3"/></svg><b>21%</b></div>
<div class="bar-row"><span>50–59</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="40%" height="10" rx="3"/></svg><b>40%</b></div>
<div class="bar-row"><span>60–79</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="67%" height="10" rx="3"/></svg><b>67%</b></div>
<div class="bar-row"><span>80–127</span><svg class="bar" focusable="false"><rect class="bar-track" width="100%" height="10" rx="3"/><rect width="86%" height="10" rx="3"/></svg><b>86%</b></div>
</div>
<figcaption>Basic Pitch 寫出的音力度越低，越可能是錯的。具體數量見下表。</figcaption>
</figure>
<div class="table-scroll" role="region" aria-label="Basic Pitch 音符力度與正確率的關係" tabindex="0">
<table>
<caption>Basic Pitch 力度與正確率，共 2,715 個音符</caption>
<thead>
<tr><th scope="col">力度</th><th scope="col" class="num">音符數</th><th scope="col" class="num">佔比</th><th scope="col" class="num">正確率</th></tr>
</thead>
<tbody>
<tr><th scope="row">低於 50</th><td class="num">247</td><td class="num">9.1%</td><td class="num">21%</td></tr>
<tr><th scope="row">50–59</th><td class="num">250</td><td class="num">9.2%</td><td class="num">40%</td></tr>
<tr><th scope="row">60–79</th><td class="num">872</td><td class="num">32.1%</td><td class="num">67%</td></tr>
<tr><th scope="row">80–127</th><td class="num">1,346</td><td class="num">49.6%</td><td class="num">86%</td></tr>
</tbody>
</table>
</div>
<p>用力度來區分音符對錯，AUC 為 0.77（0.5 相當於擲硬幣，1.0 為完美）；換成音符時長只有 0.53，所以音短並不是有用的預警訊號。把力度低於 50 的音全部刪掉，總體起音 F1 能從 0.744 升到 0.765，可這些音裡每五個就有一個是對的。所以 ScoreStarling 的樂譜檢查只把它們列為存疑，而不是直接刪除，最後交給你的耳朵判斷。如果你自己執行 Basic Pitch，在鋼琴捲軸裡按力度給音符著色或排序，也能得到同樣的待查清單。</p>

<h2 id="accuracy-claims-disagree">為什麼各家公布的準確率對不上？</h2>
<p>因為它們在不同的錄音上測的是不同的東西，有的甚至沒說明測的是什麼。</p>
<div class="table-scroll" role="region" aria-label="公開的準確率數據與各自的測試資料" tabindex="0">
<table>
<caption>公開數據，2026年10月3日核對</caption>
<thead>
<tr><th scope="col">來源</th><th scope="col">數據</th><th scope="col">測試資料</th></tr>
</thead>
<tbody>
<tr><th scope="row"><a href="https://arxiv.org/abs/2203.09893">Basic Pitch 論文</a>（Bittner 等，2022）</th><td>起音 F1 0.79；算上結束點為 0.56</td><td>GuitarSet 測試集，72 段錄音</td></tr>
<tr><th scope="row"><a href="https://arxiv.org/abs/2607.08168">MuScriptor 論文</a>（Rouard 等，2026）</th><td>起音 F1 60.4（百分制），YourMT3+ 為 32.52</td><td>作者自有資料中 372 段經過精細標註的真實錄音</td></tr>
<tr><th scope="row"><a href="https://scorecloud.com/learn/how-accurate-is-automatic-music-transcription/">ScoreCloud 指南</a></th><td>「音高準確率通常在 85-95%」</td><td>獨奏樂器和人聲；未說明方法</td></tr>
<tr><th scope="row">本次測試</th><td>起音 F1：Basic Pitch 0.744，Mirelo 0.471</td><td>12 段 GuitarSet 錄音</td></tr>
</tbody>
</table>
</div>
<p>只有指標、容許誤差和錄音都一致時，數字才有可比性。我們測出的 Basic Pitch 成績可以和它論文裡的放在一起看，因為兩者都是在 GuitarSet 上算起音 F1，只是子集不同；但不能和 MuScriptor 的放在一起比。ScoreCloud 的頁面沒說音高準確率是怎麼算的、測了多少段錄音，也沒說時間是否計入。MuScriptor 是一個開放的研究模型，Mirelo 的 Audio-to-MIDI Pro 就是從它發展而來的，Mirelo 稱其正式上線的模型更準確。Mirelo 在 2026年9月17日發表的文章中說，更新後的模型「在我們的評估中」提高了音符和樂器辨識的準確率，但沒有公布具體數字。</p>

<h2 id="accurate-notes-readable-score">音符準了，就能得到好讀的樂譜嗎？</h2>
<p>不能。起音 F1 比的是以秒計時的音符，完全不管寫出來的樂譜好不好讀。一份樂譜還需要調號、合理的升降號、寫出來的休止符、聲部和拍號，而 Basic Pitch 這類模型輸出的純 MIDI 一樣都沒有。基於同樣的 Basic Pitch 音符，我們最初的記譜程式在全部 12 個案例中寫出的樂譜都沒有調號，也看不到休止符；MuseScore Studio 的 MIDI 匯入功能則在 12 個案例中有 10 個寫出了標註的調，並顯示了所有休止符。Mirelo 自己輸出的 MusicXML，在它完成的 9 個案例中有 5 個寫出了標註的調，而且全都記在單行高音譜表上。</p>
<p>扒譜得分高，樂譜照樣可能難讀；頁面整潔，也照樣可能藏著錯音。完整的前後比較見《<a href="https://scorestarling.com/zh-hant/blog/why-midi-imports-look-messy">為什麼 MIDI 匯入 MuseScore 後亂成一團</a>》。</p>

<h2 id="which-engine-to-use">該選哪個扒譜引擎？</h2>
<p>看錄音來選。以下是我們對這些結果的解讀：</p>
<ul>
<li>單一樂器或單條旋律：選 Basic Pitch。它在吉他獨奏上遙遙領先，免費開源，而且<a href="https://github.com/spotify/basic-pitch">它的 README</a> 也說一次只處理一件樂器時效果最好。</li>
<li>樂團，或者需要分出各個聲部或鼓的混音：選 Mirelo 這類多樂器模型，因為 Basic Pitch 只會寫出一個合併的聲部。</li>
<li>吉他伴奏：兩個都行。它們的平均分接近，在不同錄音上各有勝負；如果選哪個很重要，就拿一小段兩個都試試。</li>
<li>人聲：我們還沒有測。鋼琴：我們只在合成音訊上測過自己的鋼琴獨奏選項，所以沒法給鋼琴扒譜引擎排名。</li>
</ul>
<p>無論用哪個引擎，節奏和音符時值都要用耳朵再核對一遍。上面 10月2日的測量和 10月4日的重測都不包括我們的鋼琴獨奏選項。ScoreStarling 扒單一樂器或人聲用 Basic Pitch，扒鋼琴獨奏用一個鋼琴模型，兩者都免費；扒樂團用 Mirelo，需要消耗點數（2026年10月7日核實）。每種都只處理上傳檔案的前五分鐘，檔案最大 100 MiB。人聲或單一樂器預設按 4/4 拍記譜；鋼琴獨奏會自己判斷拍號；樂團樂譜的拍號由演奏推斷，或在開始前選定。吉他、貝斯和烏克麗麗聲部也可以顯示為 TAB 譜，但不記推弦、滑音和搥弦；詳見<a href="https://scorestarling.com/zh-hant/how-it-works#specs">規格說明</a>。</p>


<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://zenodo.org/records/3371780">GuitarSet 1.1.0</a>——Xi、Bittner、Pauwels、Ye 和 Bello，Zenodo（CC BY 4.0）</li>
<li><a href="https://guitarset.weebly.com/">GuitarSet 專案網站</a>——GuitarSet 作者團隊</li>
<li><a href="https://github.com/marl/GuitarSet/issues">GuitarSet 的 issue 列表</a>——GitHub 上的 marl/GuitarSet</li>
<li><a href="https://zenodo.org/records/4599666">Slakh2100</a>——Manilow、Wichern、Seetharaman 和 Le Roux，Zenodo（CC BY 4.0）</li>
<li><a href="https://arxiv.org/abs/1909.08494">Cutting Music Source Separation Some Slakh</a>——Manilow 等，WASPAA 2019</li>
<li><a href="https://arxiv.org/abs/2203.09893">A Lightweight Instrument-Agnostic Model for Polyphonic Note Transcription and Multipitch Estimation</a>——Bittner 等，Spotify，2022</li>
<li><a href="https://github.com/spotify/basic-pitch">Basic Pitch</a> 及其 <a href="https://github.com/spotify/basic-pitch/blob/main/basic_pitch/note_creation.py">note_creation.py</a>——Spotify，GitHub</li>
<li><a href="https://mir-eval.readthedocs.io/latest/api/transcription.html">mir_eval.transcription</a>——mir_eval 說明文件</li>
<li><a href="https://arxiv.org/abs/2607.08168">MuScriptor: An Open Model for Multi-Instrument Music Transcription</a>——Rouard 等，2026</li>
<li><a href="https://mirelo.ai/blog/introducing-audio-to-midi-pro">Audio-to-MIDI Pro: full mixes to editable MIDI</a>——Mirelo，2026年9月17日</li>
<li><a href="https://mirelo.ai/models/audio-to-midi">Audio to MIDI converter</a> 和 <a href="https://mirelo.ai/changelog">Changelog</a>——Mirelo</li>
<li><a href="https://scorecloud.com/learn/how-accurate-is-automatic-music-transcription/">How Accurate Is Automatic Music Transcription?</a>——ScoreCloud</li>
</ol>
</section><h2>常見問題</h2><h3>AI 能把 MP3 扒成譜嗎？</h3><p>能，可以逐個音符扒成 MIDI，只是會有錯要改。Spotify 的 Basic Pitch 這類模型最擅長單一樂器；完整的混音需要多樂器模型，準確率也更低。在我們 2026年10月2日的測試中，成績最好的一組是 Basic Pitch 扒吉他獨奏，起音 F1 為 0.818（ScoreStarling 對它的用法做了兩處調整後，10月4日重測為 0.846），所以還是要準備憑耳朵改掉一些音。</p><h3>Basic Pitch 的準確率夠不夠出樂譜？</h3><p>單一樂器出個初稿，夠了。在 12 段吉他錄音上，只看音符起點時它的起音 F1 為 0.744，把音符結束點也算上就只剩 0.508；而且它輸出的 MIDI 沒有調號、休止符和聲部，得靠記譜這一步補上。《<a href="https://scorestarling.com/zh-hant/blog/why-midi-imports-look-messy">為什麼 MIDI 匯入 MuseScore 後亂成一團</a>》一文講了這一步會改動什麼。</p><h3>怎樣自己測扒譜的準確率？</h3><p>你需要一份信得過的參考標註，音符時間以秒為單位。有了它，就能用開源的 mir_eval 函式庫，依我們採用的容許誤差替音符配對評分：起點 50 毫秒，音高 50 音分，結束點為音符時長的 20% 或 50 毫秒。沒有參考標註的話，就把短樂段重複播放，憑耳朵對照樂譜和錄音。</p><h3>為什麼 Mirelo 扒某些吉他獨奏時一個音都沒出？</h3><p>我們也不知道。2026年10月2日，六段獨奏錄音中有三段傳回了空結果。當時我們沒有提供 Mirelo 的可選樂器列表，之後也沒有重試。在伴奏上，它的成績接近 Basic Pitch；在多樂器片段上，它領先。</p><h3>AI 扒人聲或鋼琴準不準？</h3><p>人聲方面，我們暫時還給不出自己的數據。鋼琴方面，只在合成音訊上測過：25 首樂曲的開頭幾個小節，每首以彈性速度演奏兩遍（共 50 段演奏，2026年10月6日測量）。我們以 Kong 等人的鋼琴模型為基礎的鋼琴獨奏選項，辨識出了約 92% 的譜面音符，但 50 段裡只有 35 段的拍子找對了，拍子、拍號和弱起小節全對的只有 26 段。Basic Pitch 的作者把它設計成適用於各種樂器，也包括人聲。無論用哪個模型，清晰、乾聲的單一人聲或單一樂器錄音，都最有機會扒準。</p>]]></content:encoded>
    </item>
    <item>
      <title>MIDI 匯入 MuseScore 為什麼亂七八糟？怎麼解決</title>
      <link>https://scorestarling.com/zh-hant/blog/why-midi-imports-look-messy</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/why-midi-imports-look-messy</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>研究</category>
      <description>MIDI 存的是演奏，不是樂譜。本文用 12 段吉他轉譜說明這會造成什麼問題、MuseScore 為什麼會重新猜調和速度，以及能解決大部分問題的前處理方法。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>MIDI 檔案記錄的是一次演奏：按了哪個鍵、什麼時候按、按多重、按多久。它的音符沒有升降記號、聲部、書面休止符或符槓，檔案裡也常常沒有調號，所以 MuseScore 只能去猜，而音符偏離節拍網格時，它猜得最離譜。在 12 段吉他轉譜上，我們最早的轉換器一個調號也沒寫，還藏起了所有休止符；同樣的音符先對齊到十六分音符網格、使用固定速度、在每條音軌寫入調號，再交給 MuseScore Studio 4.7.5 的匯入器，12 份裡有 10 份調號正確，所有休止符也都顯示了出來。MuseScore Studio 4 沒有 MIDI 匯入面板，所以這些修正不是寫進 MIDI 檔案，就是寫進命令列用的選項檔。</p>
</section>

<h2 id="what-midi-does-not-store">MIDI 檔案沒有存哪些資訊？</h2>
<p>讀譜需要的大部分資訊，它都沒有存。一個 MIDI 音符就是 0 到 127 之間的一個鍵號加一個力度值，在音符開始時送出一次，結束時再送出一次（<a href="https://midi.org/summary-of-midi-1-0-messages">MIDI 協會</a>）。63 號音既是 D♯ 也是 E♭，該寫哪個取決於調性，而音符本身並不說明。標準 MIDI 檔案可以用 <a href="https://mido.readthedocs.io/en/latest/meta_message_types.html">meta 事件</a>加入速度、拍號和調號，但聲部、休止符、符槓和連音都沒有標準的存放位置。休止符只是一段空白，音長也只是 tick 數，而不是書面時值。</p>
<p>音訊轉 MIDI 工具還會帶來第二個問題：它們的時間來自錄音，音符很少正好落在拍點上。ScoreStarling 使用的 Spotify Basic Pitch 會依你給定的速度，把所有音符寫進同一個 Electric Piano 1（電鋼琴 1）聲部，而且不寫調號（<a href="https://github.com/spotify/basic-pitch/blob/main/basic_pitch/note_creation.py">note_creation.py</a>）。要把它變成樂譜，就得為每個音符選定音名寫法、書面時值、聲部和譜表。每個轉換器都得做這些選擇，差別只在於做得好不好。</p>

<h2 id="simple-converter-results">簡單的轉換器把 12 段吉他轉譜弄成什麼樣子</h2>
<p>我們最早的記譜步驟，是一個以 music21 函式庫自行編寫的轉換器。我們把公開資料集 GuitarSet 中 12 段錄音經 Basic Pitch 轉出的 MIDI 交給它，同時提供每段錄音的速度，再由 Verovio 原樣排版它寫出的內容。逐一檢查這 12 份樂譜，我們發現：</p>
<ul>
<li>所有樂譜都看不到休止符：music21 的 MusicXML 匯出器預設用隱藏的休止符填補空白（<a href="https://github.com/cuthbertLab/music21/blob/master/music21/musicxml/m21ToXml.py">m21ToXml.py</a>）。</li>
<li>所有樂譜都沒有調號。E 大調的曲子一個升記號都沒有；一段 C 大調伴奏的 431 個書面音符裡，有 153 個帶著多餘的還原記號（對應 421 個 MIDI 音符，有些被拆開後用連結線相連）。</li>
<li>音名寫法只看音符編號。MIDI 63 號音一律寫成 E♭，連在本該寫 D♯ 的 E 大調裡也一樣；在一首 G♭ 大調的曲子裡，49 個調內音中有 30 個寫錯了。</li>
<li>一行譜表上最多五個聲部。音符的起點和終點各自對齊到最近的十六分音符，於是下一個音彈出時仍在振動的那根弦，就逼出了另一個聲部，還配上了相應的附點和連結線。</li>
<li>吉他被寫成了鋼琴：12 份樂譜中有 10 份用鋼琴大譜表，另外 2 份用低音譜號，沒有一份用吉他譜號，因為 Basic Pitch 把所有音符都標成了 Electric Piano（電鋼琴）。</li>
</ul>
<p class="note">2026年10月7日，我們用同一批存下來的 MIDI 檔案重新執行了這個轉換器，得到的數字完全一樣。</p>
<p>問題不在音樂字型。Verovio 和它的 Leipzig 字型只是照著 MusicXML 畫；錯的是 MusicXML 本身。</p>

<h2 id="before-and-after">前後比較：改用 MuseScore Studio 的匯入器</h2>
<p>我們先看了開源的替代方案：partitura 能處理調性、音名寫法和聲部，但沒有量化器，也不寫休止符；我們找到的其他研究工具，不是只支援鋼琴，就是沒有標明授權、僅限非商業用途，或者尚未釋出。於是我們把這項工作交給了 MuseScore Studio 4.7.5 的 MIDI 匯入器，以獨立程式的方式執行。同樣的 12 段錄音、同樣的 Basic Pitch 音符、同樣的給定速度，依下文的方法前處理，2026年10月2日在一台 Mac 上測得：</p>
<div class="table-scroll" role="region" aria-label="12 段 GuitarSet 轉譜的排版前後比較" tabindex="0">
<table>
<caption>12 段 GuitarSet 轉譜的前後比較</caption>
<thead>
<tr><th scope="col">指標</th><th scope="col" class="num">music21 轉換器</th><th scope="col" class="num">MuseScore 匯入器</th></tr>
</thead>
<tbody>
<tr><th scope="row">調號與標註一致</th><td class="num">0/12</td><td class="num">10/12</td></tr>
<tr><th scope="row">調內音寫法與調性不符</th><td class="num">2,703 個中 322 個</td><td class="num">3,213 個中 0 個</td></tr>
<tr><th scope="row">每個書面音符的變音記號數</th><td class="num">0.53</td><td class="num">0.06</td></tr>
<tr><th scope="row">可見的休止符</th><td class="num">1,626 個中 0 個</td><td class="num">全部 988 個</td></tr>
<tr><th scope="row">單行譜表最多聲部數</th><td class="num">5</td><td class="num">2</td></tr>
<tr><th scope="row">所有 MIDI 音符都有寫出</th><td class="num">12/12</td><td class="num">12/12</td></tr>
<tr><th scope="row">4/4 拍、使用給定速度</th><td class="num">12/12</td><td class="num">12/12</td></tr>
</tbody>
</table>
</div>
<p>兩邊的書面音符總數不同，是因為 MuseScore 用連結線跨拍、跨小節線連接音符的方式不一樣。兩個錯的調都和標註差一個五度：G♭ 大調的曲子寫成了 D♭ 大調，E 大調的寫成了 B 大調。檔案裡沒有我們推估的調時，MuseScore 自己猜的調只對了 5 份（2026年10月7日重測）；正是把推估結果寫進檔案，才把它提高到 10 份。匯入器處理每段錄音花了 1.2–4.3 秒，平均 2.3 秒。</p>

<h2 id="why-musescore-reguesses">MuseScore 為什麼會重新猜速度、拍號和調？</h2>
<p>因為在它看來，沒量化的 MIDI 就像一段現場演奏。匯入器會檢查落在拍點附近的音符，如果其中正好從拍點開始的不到 60%，它就把檔案當作真人演奏（<a href="https://github.com/musescore/MuseScore/blob/master/src/importexport/midi/internal/midiimport/importmidi_quant.cpp">importmidi_quant.cpp</a>）。接著它會執行自己的節拍追蹤，選定拍號，再拉伸音符的時間去貼合它找到的拍子。MuseScore 的舊版手冊說，這種模式是拿準確性換可讀性，對沒有對齊的檔案預設開啟。</p>
<p>調性另有一條規則。只要有任何一條非鼓音軌缺少調號，或者檔案被判定為演奏，匯入器就會根據音符推估調性（<a href="https://github.com/musescore/MuseScore/blob/master/src/importexport/midi/internal/midiimport/importmidi_key.cpp">importmidi_key.cpp</a>）。所以只在速度音軌裡寫調號是不夠的，每條有音符的音軌都要有。直接匯入 Basic Pitch 的原始 MIDI 時，我們看到拍號、速度和調全都被重新猜了一遍，就算已經把「真人演奏」選項設為 false 也一樣。</p>

<h2 id="prepare-midi">匯入前該怎麼處理 MIDI？</h2>
<p>能在 MIDI 檔案裡決定的記譜細節，就在 MuseScore 看到它之前決定好。我們對每份轉譜都會這樣做：</p>
<ol>
<li>使用一個固定的速度。ScoreStarling 會用你設定的 BPM，或從錄音中偵測到的速度；在 DAW（編曲軟體）裡，可以跟著節拍器錄音，或者把專案速度調到與這段錄音一致。</li>
<li>把每個音符的起點和終點都移到網格上，網格精度就是你想讀到的最短時值。本文 10月2日的測試用的是 4/4 拍下的十六分音符。所有音符都在網格上時，MuseScore 會把檔案當作寫好的樂譜來讀，而不是當作演奏。</li>
<li>把速度和拍號放在第一條音軌裡，每條有音符的音軌都寫上調號。我們用 music21 的 Krumhansl–Schmuckler 調性辨識演算法，根據音符推估調性；如果你知道真正的調，就直接用它。</li>
<li>幫每條音軌設定真實的樂器。MuseScore 會根據 General MIDI 音色來選擇譜表和譜號：仍標為 Electric Piano 的吉他會被放到鋼琴大譜表上，而設為尼龍弦吉他音色時，則會用下方帶 8 的高音譜號。</li>
<li>注意同音反覆。如果同一個音在一個網格單位內彈了兩下，對齊後兩次起音會合併成一次；我們會把第二次往後挪一個三十二分音符。</li>
</ol>

<h2 id="musescore-import-settings">MuseScore 匯入該用哪些設定？</h2>
<p>MuseScore Studio 4 沒有可以設定這些選項的匯入面板。它的手冊說 MIDI 匯入面板「目前尚未實作」，要求恢復它的 GitHub issue（<a href="https://github.com/musescore/MuseScore/issues/11918">#11918</a>，2022年6月提出）在我們 2026年10月3日查看時仍未關閉。在軟體裡，唯一和量化有關的設定是匯入 MIDI 檔案時的最短音符時值，位於「偏好設定 → 匯入」（Preferences → Import）。</p>
<p>匯入器仍會從命令列裡用 <code>-M</code> 傳入的檔案讀取主要選項，<a href="https://handbook.musescore.org/appendix/command-line-usage">手冊的命令列頁面</a>附有一個範例。下面是 10月2日測試用的檔案；現在 ScoreStarling 會把旋律寫在一個聲部裡（<code>VoiceCount</code> 設為 0），並在第一小節是弱起小節時開啟 <code>RecognizePickupBar</code>：</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;MidiOptions&gt;
  &lt;QuantValue&gt;2&lt;/QuantValue&gt;
  &lt;VoiceCount&gt;1&lt;/VoiceCount&gt;
  &lt;Duplets&gt;false&lt;/Duplets&gt;
  &lt;Triplets&gt;false&lt;/Triplets&gt;
  &lt;Quadruplets&gt;false&lt;/Quadruplets&gt;
  &lt;Quintuplets&gt;false&lt;/Quintuplets&gt;
  &lt;Septuplets&gt;false&lt;/Septuplets&gt;
  &lt;Nonuplets&gt;false&lt;/Nonuplets&gt;
  &lt;HumanPerformance&gt;false&lt;/HumanPerformance&gt;
  &lt;MeasureCount2xLess&gt;false&lt;/MeasureCount2xLess&gt;
  &lt;SplitStaff&gt;false&lt;/SplitStaff&gt;
  &lt;ClefChanges&gt;false&lt;/ClefChanges&gt;
  &lt;SimplifyDurations&gt;true&lt;/SimplifyDurations&gt;
  &lt;ShowStaccato&gt;false&lt;/ShowStaccato&gt;
  &lt;DottedNotes&gt;true&lt;/DottedNotes&gt;
  &lt;RecognizePickupBar&gt;false&lt;/RecognizePickupBar&gt;
  &lt;Swing&gt;0&lt;/Swing&gt;
&lt;/MidiOptions&gt;</code></pre>
<p>接著進行轉換，輸出檔使用 MusicXML 副檔名：</p>
<pre><code>mscore -M midi-import.xml -o score.musicxml performance.mid</code></pre>
<p>程式的名稱和位置依系統而異，Windows 上手冊用的是 MuseScore4.exe。主要選項的作用如下：</p>
<div class="table-scroll" role="region" aria-label="MuseScore 的 MIDI 匯入選項及其作用" tabindex="0">
<table>
<caption>我們的 MIDI 匯入選項</caption>
<thead>
<tr><th scope="col">選項</th><th scope="col">設定值</th><th scope="col">作用</th></tr>
</thead>
<tbody>
<tr><th scope="row">QuantValue</th><td>2</td><td>最短書面時值：十六分音符（1 為八分音符，3 為三十二分音符）</td></tr>
<tr><th scope="row">VoiceCount</th><td>1</td><td>每行譜表最多兩個聲部；聲部數等於設定值加一</td></tr>
<tr><th scope="row">Triplets 及其他連音選項</th><td>false</td><td>不辨識連音，因為我們的網格裡沒有連音</td></tr>
<tr><th scope="row">HumanPerformance</th><td>false</td><td>不把檔案當作現場演奏；只有音符都在網格上時，這項設定才成立</td></tr>
<tr><th scope="row">SplitStaff</th><td>false，鋼琴為 true</td><td>把鋼琴聲部依左右手分到兩行譜表；對其他樂器，MuseScore 則會拆成獨立的聲部</td></tr>
<tr><th scope="row">SimplifyDurations</th><td>true</td><td>更少的休止符，更簡單的時值</td></tr>
<tr><th scope="row">ClefChanges</th><td>false</td><td>譜表中途不插入小號的變換譜號</td></tr>
<tr><th scope="row">RecognizePickupBar</th><td>false</td><td>第一小節視為完整小節</td></tr>
<tr><th scope="row">Swing</th><td>0</td><td>不偵測搖擺節奏</td></tr>
</tbody>
</table>
</div>
<p>如果你的音樂裡有三連音，就不要對齊到直拍網格，並保持 Triplets 開啟；MuseScore 辨識三連音的效果如何，我們還沒測過。在 MuseScore 3 裡，這些設定都放在一個匯入面板中，欄位名稱有 Max. voices、Search tuplets 和 Is human performance 等。</p>

<h2 id="trade-offs">譜面更整齊，代價是什麼？</h2>
<p>會損失一些準確性，主要在音符時值上。以下是我們測得或已知的取捨：</p>
<ul>
<li>MuseScore 把 26% 的音符寫得比量化後的演奏更長，最多長出三個十六分音符，從來沒有寫短過；同時發聲但長短不同的音符，也可能被寫成同一個時值。在 ScoreStarling 裡，播放和 MIDI 匯出保留演奏的實際時間，不過旋律線會先清理一遍：同時彈下的幾個音只留最強的那個，微弱的回音會去掉，延續的音在下一個音開始時結束。</li>
<li>調性是根據音符推估的，12 份裡錯了 2 份，每次都差一個五度。帶七個升記號或降記號的調，會寫成對應的、只有五個升降記號的等音調。</li>
<li>10月2日這次吉他測試用的是直拍的十六分音符網格，表示不了三連音和搖擺節奏。ScoreStarling 轉譜單一樂器或人聲時依然如此，拍號從 4/4 開始；鋼琴獨奏則會跟隨演奏的小節劃分，可以選用 4/4、3/4、6/8 或 12/8。如果你自己準備檔案，請寫入真實的拍號。</li>
<li>MuseScore 會依自己的規則把鋼琴和弦分給左右手，即使是兩個音靠得很近的和弦，也可能被分到不同的譜表上。</li>
</ul>
<p>其中一部分可以在匯入後整理。<a href="https://handbook.musescore.org/notation/rhythm-meter-and-measures/regroup-rhythms">工具 → 重組節奏</a>（Tools → Regroup rhythms）會改寫選取的段落，讓音符按拍子分組，節奏本身不變。<a href="https://handbook.musescore.org/notation/pitch/respell-pitches">工具 → 用升記號／降記號重新拼寫音高</a>（Tools → Respell pitches with sharps / with flats）可以修改音名寫法，不過手冊說它是針對同一音高的音符設計的。調號錯了，在匯入前改 MIDI 檔案，比匯入後一個一個改音符快得多。</p>

<h2 id="mp3-to-musescore">MuseScore 能把 MP3 轉成樂譜嗎？</h2>
<p>MuseScore Studio 不行：它能開啟 MusicXML、MIDI 這類樂譜格式，但打不開音訊。Muse Group 於 2026年8月17日在 MuseScore 網頁平台上推出了 <a href="https://www.mu.se/posts/musescore-audio-score-features">Audio-to-Score 測試版</a>：支援鋼琴獨奏和木吉他，MP3 檔案最大 30 MB（約三分鐘），免費，結果以 MSCZ 檔案回傳，可在 MuseScore Studio 開啟。我們還沒有測試過。</p>
<p>另一條路就是本文介紹的做法：先用音訊轉 MIDI 模型，再調整它的時間，最後交給 MuseScore 的匯入器。ScoreStarling 轉單一樂器或人聲時就是這樣，用的是 Basic Pitch；鋼琴獨奏則用一個鋼琴模型，它轉出的音符同時提供節拍和小節線；頁面由 Verovio 繪製。現在，單音旋律會寫在一行譜表的一個聲部裡，彈和弦的聲部會把和弦保留在同一行譜表上，「樂譜設定 → 版面」也可以改變單聲部錄音的記譜方式。上面的測量結果來自 10月2日。10月4日，在旋律改為一次一個音、寫在一行譜表上之後，我們重測了一遍：調號同樣 12 份對了 10 份，3,158 個調內音裡寫法與調性不符的為 0，357 個休止符全部顯示。</p>
<p>你也可以在 ScoreStarling 裡選擇或拖入 MIDI 檔案，直接得到可編輯的樂譜；鼓軌會被略過，整份樂譜只用一個拍號。錄音只轉前五分鐘，上傳檔案最大 100 MiB。PDF 免費，MusicXML 需要先解鎖樂譜（一次 15 點）；其餘細節請見<a href="https://scorestarling.com/zh-hant/how-it-works#specs">技術規格</a>（2026年10月7日核對）。<a href="https://scorestarling.com/app">工作區</a>可以免費開始使用；標明日期的吉他和樂團測量結果，收錄在《<a href="https://scorestarling.com/zh-hant/blog/ai-music-transcription-accuracy">AI 扒譜到底有多準？我們實測了一下</a>》一文中。</p>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://midi.org/summary-of-midi-1-0-messages">Summary of MIDI 1.0 Messages</a>——MIDI Association</li>
<li><a href="https://mido.readthedocs.io/en/latest/meta_message_types.html">Meta Message Types</a>——mido 說明文件</li>
<li><a href="https://github.com/spotify/basic-pitch/blob/main/basic_pitch/note_creation.py">basic_pitch/note_creation.py</a>——Spotify Basic Pitch，GitHub</li>
<li><a href="https://github.com/cuthbertLab/music21/blob/master/music21/musicxml/m21ToXml.py">music21/musicxml/m21ToXml.py</a>——music21，GitHub</li>
<li><a href="https://zenodo.org/records/3371780">GuitarSet 1.1.0</a>——Xi 等，Zenodo（CC BY 4.0）</li>
<li><a href="https://musescore.org/en/handbook/4/midi-import">MIDI import</a>——MuseScore 4 手冊</li>
<li><a href="https://github.com/musescore/MuseScore/issues/11918">Restore MIDI import panel, issue #11918</a>——MuseScore，GitHub</li>
<li><a href="https://github.com/musescore/MuseScore/blob/master/src/importexport/midi/internal/midiimport/importmidi_quant.cpp">importmidi_quant.cpp</a> 和 <a href="https://github.com/musescore/MuseScore/blob/master/src/importexport/midi/internal/midiimport/importmidi_key.cpp">importmidi_key.cpp</a>——MuseScore 原始碼，GitHub</li>
<li><a href="https://musescore.org/en/print/book/export/html/22918">MIDI import</a>——早期 MuseScore 手冊（匯入面板）</li>
<li><a href="https://handbook.musescore.org/appendix/command-line-usage">Command line usage</a> 及其中的<a href="https://musescore.org/sites/musescore.org/files/midi_import_options_0.xml">MIDI 匯入選項範例檔</a>——MuseScore Studio 手冊</li>
<li><a href="https://handbook.musescore.org/customization/preferences">Preferences</a>、<a href="https://handbook.musescore.org/file-management/file-export">File export</a>、<a href="https://handbook.musescore.org/notation/rhythm-meter-and-measures/regroup-rhythms">Regroup rhythms</a> 和 <a href="https://handbook.musescore.org/notation/pitch/respell-pitches">Respell pitches</a>——MuseScore Studio 手冊</li>
<li><a href="https://www.mu.se/posts/musescore-audio-score-features">Hear It, Play It: MuseScore Introduces New Smart Audio Recognition Features</a>——Muse Group，2026年8月17日</li>
</ol>
</section><h2>常見問題</h2><h3>怎麼把 MIDI 轉成 MusicXML？</h3><p>在 MuseScore Studio 裡開啟 MIDI 檔案，選擇「檔案 → 匯出」（File → Export），格式選 MusicXML，預設會匯出壓縮的 .mxl。也可以在終端機執行 mscore -o score.musicxml file.mid，效果相同；加上 -M 還能指定一個選項檔，設定量化和聲部。記得先把 MIDI 處理好，否則 MusicXML 裡也會一樣亂。</p><h3>為什麼 MIDI 匯入後滿是連結線和附點音符？</h3><p>因為真人演奏的音符很少剛好在拍點上結束。對齊到很細的網格後，它們會跨過拍子和小節線，只能用連結線連起來；互相重疊的音符還會跑到額外的聲部裡。把量化精度設為你想讀到的最短時值，把匯入器限制為最多兩個聲部，並讓它簡化時值；這樣 MuseScore 會把部分音符寫得稍長一點，讓譜面更整齊。</p><h3>為什麼我以為是 E♭，MuseScore 卻寫成了 D♯？</h3><p>MIDI 只記錄 63 號音，不記錄它該寫成哪個音名，所以 MuseScore 會依調號決定寫法；某條音軌沒有調號時，它就自己推估調性。匯入前，先在每條音軌裡寫好正確的調號。匯入後，可以用「工具 → 用升記號／降記號重新拼寫音高」（Tools → Respell pitches with sharps / with flats）修改選取的同音高音符。</p><h3>MuseScore Studio 4 還能用 MuseScore 3 的 MIDI 匯入面板嗎？</h3><p>截至 2026年10月3日還不行：這個面板沒有移植過來，要求恢復它的 <a href="https://github.com/musescore/MuseScore/issues/11918">GitHub issue #11918</a> 也還沒關閉。MuseScore Studio 4 保留了一項針對匯入 MIDI 的「最短音符」偏好設定，它的命令列也接受用 -M 傳入選項檔，能涵蓋原本面板的主要設定。</p><h3>ScoreStarling 的轉譜結果能在 MuseScore 裡開啟嗎？</h3><p>可以。匯出 MusicXML（.musicxml 或 .mxl），調號、音名寫法、休止符和聲部都會保留；如果只需要音符，匯出 MIDI 也行。兩者都要先解鎖樂譜，只需花一次 15 點。ScoreStarling 可以免費開始使用；每份轉譜結果都只是初稿，需要你用耳朵核對。</p>]]></content:encoded>
    </item>
    <item>
      <title>如何把語音備忘錄轉成樂譜</title>
      <link>https://scorestarling.com/zh-hant/blog/voice-memo-to-sheet-music</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/voice-memo-to-sheet-music</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>指南</category>
      <description>用穩定的速度錄下一段人聲，匯出 .m4a 檔案，填寫 BPM 或交給系統自動偵測，再用耳朵逐段核對初稿。本文附上錄音技巧、操作步驟和目前的限制。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>單獨錄下旋律：只有一個人聲或一件樂器，手機離得近，房間安靜，速度穩定，最好戴著耳機聽節拍器，這樣就知道 BPM 是多少。匯出這段錄音（iPhone 預設匯出 .m4a），剪掉開頭，讓它從第一個音開始，再連同速度一起交給扒譜工具。然後把得到的樂譜當作初稿：每次重複播放兩小節，和錄音對照，修正音高和時值，最後再移調，匯出 PDF、MusicXML 或 MIDI。</p>
</section>

<h2 id="can-an-app-turn-humming-into-sheet-music">哼唱或唱歌能用 App 轉成樂譜嗎？</h2>
<p>可以，前提是錄音裡只有一條清楚的旋律。軟體要做兩件不同的事。首先是聽出音符：每個音的音高，從什麼時候開始，到什麼時候結束。然後把這些音符寫下來，也就是決定調、拍號、音符時值和小節線的位置。如果旋律還只在你腦子裡，就先對著手機唱出來；下面說的一切都適用於這段錄音。</p>
<p>在 ScoreStarling 裡，單一樂器或人聲的第一步由 Spotify 開源的 Basic Pitch 模型完成，它在我們的伺服器上把音訊轉成 MIDI。Spotify 稱 Basic Pitch 適用於「幾乎任何樂器，包括人聲」。這是他們的說法。我們自己目前測過的是吉他錄音、一段樂團片段和合成的鋼琴，沒有測過人聲。第二步交給 MuseScore Studio 4.7.5 的 MIDI 匯入功能，最後由 Verovio 排版。</p>
<p>節奏往往是語音備忘錄裡最難處理的部分。ScoreStarling 會偵測速度，或者使用你填寫的 BPM，但對單一樂器或人聲，它仍然從 4/4 拍開始、按均分的網格記譜，所以速度飄移、旋律前的空白或者速度不對，都可能讓連結線和休止符放錯位置。鋼琴獨奏會跟隨演奏本身的律動和拍子，但它是給鋼琴用的，不適合唱出來的錄音。下面的建議能幫旋律保持穩定的律動。</p>

<h2 id="how-to-record-a-voice-memo-for-transcription">用來扒譜的語音備忘錄該怎麼錄？</h2>
<p>給軟體一條旋律：速度穩定，離麥克風近，沒有別的聲音同時在響。</p>
<ul>
<li>只錄一個人聲或一件樂器：不放伴奏，不要第二個人一起唱，人聲下面也不要墊吉他。混音太滿、殘響太重、幾件樂器同時演奏，都會讓初稿更難看懂。</li>
<li>手機靠近一點，房間保持安靜。Apple 的使用手冊說，錄音音量靠距離來調整：把手機移近或移遠聲源。要近，但別近到最響的音會失真。有窗簾、地毯這類布料的房間，回音比浴室或樓梯間小。</li>
<li>用一個你聽得見、手機錄不到的節拍：用另一台裝置播放節拍器，戴耳機聽。Apple 的使用手冊說，如果 iPhone 上的其他 App 開始播放音訊，「語音備忘錄」就會停止錄音，所以節拍器不能放在正在錄音的這支手機上。</li>
<li>記下 BPM。如果錄音時沒開節拍器，事後可以用節拍器的 Tap 測速功能跟著點，或者數一數 15 秒內有幾拍，再乘以 4。</li>
<li>預備拍在心裡數。說出口的「一、二、三、四」和其他聲音沒有區別，可能會被寫成音符，所以請在心裡或耳機裡數，或者事後把預備拍剪掉。</li>
<li>用「da」或「doo」這樣的音節來唱，不要閉著嘴哼，這樣重複的音不會糊成一個。這是我們的建議，不是測出來的結論。</li>
<li>每段錄音控制在 5 分鐘以內：檔案最大 100 MiB，只會轉前 5 分鐘。長的歌曲請分段錄，每段都從小節線開始。</li>
<li>額外功能都別開。在 Pixel 9 及後續機型上，Google 的「錄音工具」可以用 AI 為錄音加上背景音樂，而加進去的音樂也會變成要轉譜的聲音。如果你用「語音備忘錄」的音層錄音在伴奏上唱過，先分隔音層，只上傳人聲。</li>
</ul>
<p>然後剪掉開頭。第 1 小節從檔案的 0:00 開始，所以要把開頭的空白或預備拍剪掉，讓檔案正好從第一個音開始。如果曲子是弱起，之後可以把第一小節標成弱起小節。在「語音備忘錄」裡，用編輯畫面中黃色的裁剪控點就能剪，分開手指可以放大波形，剪得更精確。Google「錄音工具」有「裁剪及移除」功能，會另存一份裁剪後的副本。</p>

<h2 id="what-format-is-a-voice-memo">iPhone 和 Android 的語音備忘錄是什麼格式？</h2>
<p>iPhone 的「語音備忘錄」預設把錄音匯出為 .m4a 檔案；Android 手機的錄音機 App 因廠牌而異，分享出來之後看一下副檔名就知道了。</p>
<p>Apple 的《iPhone 使用手冊》（iOS 27，2026年10月3日查閱）說，錄音預設匯出為 .m4a，音層錄音會合併成一層，空間音訊會變成立體聲。要把檔案從手機裡拿出來，在錄音的分享選項裡選「儲存到檔案」。不要選「可編輯」：它匯出的是 QuickTime 音訊格式，用於日後再調整效果和音層。如果是音層錄音，先用「分隔音層」，每一層都會變成獨立的 .m4a。</p>
<p>在 Pixel 手機上，Google「錄音工具」可以把錄音當作音訊檔案分享：按住錄音檔，然後依序輕觸「分享」「檔案」「音訊」。Google 的說明頁面沒有寫明是什麼格式（2026年10月3日查閱），其他 Android 品牌也各有自己的錄音機 App。不管拿到的是什麼格式，ScoreStarling 都支援 WAV、MP3、FLAC、M4A、OGG 和 AIFF。你也可以不用檔案，直接在瀏覽器裡錄音；瀏覽器錄音到 4:59 會停止，正好在 5 分鐘的限制以內。</p>

<h2 id="voice-memo-to-sheet-music-steps">逐步教學：在 ScoreStarling 裡把語音備忘錄轉成樂譜</h2>
<p>上傳錄音，填上速度，轉譜，然後在匯出前用耳朵校對樂譜。在 ScoreStarling 上，轉單一樂器或人聲、編輯和下載 PDF 都免費；解鎖樂譜的 MusicXML 和 MIDI 要用點數（2026年10月7日確認）。開啟<a href="https://scorestarling.com/app">工作區</a>就可以開始。</p>
<ol>
<li>選擇「新增樂譜」。把檔案拖曳進來，或者選擇「錄音」，直接對著瀏覽器唱。在你按下「建立樂譜」之前，什麼都不會儲存。</li>
<li>選擇演奏的內容時，保留「單一樂器或人聲」，它會在我們的伺服器上免費執行 Basic Pitch。「鋼琴獨奏」也免費。「樂團或多種樂器」要用點數，每分鐘 40 點，開始前會先顯示價格，音訊會傳送給我們的樂團轉譜合作夥伴 Mirelo；只有你選了它才會執行。</li>
<li>開啟「更多選項」，在「每分鐘拍數」裡填寫以四分音符為一拍的速度（30 到 240），或者留在「自動」讓系統偵測。這個數值決定樂譜怎麼寫，不會改變錄音。如果拿不準是 70 還是 140，選哪個都行：填 140 的話，每個音符都會寫成兩倍的時值。</li>
<li>按下「建立樂譜」。樂譜出來後，播放會和你的錄音同步。</li>
<li>按照下一節介紹的方法，每次兩小節，用耳朵檢查。</li>
<li>聽到問題就改。選取一個音符，可以把它升高或降低半音（也可以用上下方向鍵）、修改時值，或者刪除；刪除後會留下休止符，拍子不會亂。改錯了可以復原、重做，或者回到已儲存的版本。</li>
<li>最後在「樂譜設定」裡收尾：調號和譜號、拍號，曲子如果在強拍之前就開始，還要設定弱起小節，以及依音程移調。移調放到最後：它會移動所有音符和調，但錄音仍是原來的調，移調之後再用耳朵對照就難了。</li>
<li>下載你需要的檔案：列印用 PDF，打譜軟體用 MusicXML，DAW（編曲軟體）用 MIDI，還有頁面圖片、分譜，以及 MP3 或 WAV 試聽音訊。MusicXML 和 MIDI 需要先解鎖樂譜（一次性 15 點）；解鎖之前，PDF、頁面圖片和分譜的頁尾會有一行小字。音訊是用 General MIDI 音色合成的樂譜，不是你的聲音。該選哪種檔案，請看《<a href="https://scorestarling.com/zh-hant/blog/musicxml-vs-midi-vs-pdf">MusicXML、MIDI 和 PDF 有什麼區別？你該用哪一種</a>》。</li>
</ol>

<h2 id="check-transcription-by-ear">怎樣對照錄音檢查轉譜結果？</h2>
<p>每次處理兩小節：選取它們，開啟重複播放，先用三種方式聽一遍再動手改。播放器在播放時可以隨時切換：只聽樂譜、兩者一起聽、只聽錄音。先單獨聽錄音，把旋律記牢，再單獨聽樂譜，最後兩者一起聽。音高錯了會和你的聲音打架；位置錯了的音聽起來像重複了一遍，好像回音。</p>
<p>按順序修改。先檢查小節線，因為第一拍錯了，每個小節都會錯：可以標出弱起小節，或者重新剪輯檔案再轉一次。然後改音高，再改時值，表情記號和排版留到最後。</p>
<div class="table-scroll" role="region" aria-label="語音備忘錄檢查清單" tabindex="0">
<table>
<caption>語音備忘錄檢查清單</caption>
<thead><tr><th scope="col">階段</th><th scope="col">檢查項目</th><th scope="col">原因</th></tr></thead>
<tbody>
<tr><th scope="row">錄音前</th><td>只有一個人聲或一件樂器，沒有其他聲音</td><td>「單一樂器或人聲」選項就是為單一聲部設計的</td></tr>
<tr><th scope="row">錄音前</th><td>用另一台裝置在耳機裡放節拍器；記下 BPM</td><td>自己填的 BPM 比自動估算更可靠</td></tr>
<tr><th scope="row">錄音前</th><td>手機靠近，房間安靜，回音小</td><td>雜音和殘響會讓初稿更難看懂</td></tr>
<tr><th scope="row">上傳前</th><td>剪到從第一個音開始，時長不超過帳號的上限</td><td>從清楚的拍點開始；每段最長 5 分鐘</td></tr>
<tr><th scope="row">檢查時</th><td>小節線和弱起小節</td><td>第一拍錯了，每個小節都會跟著錯位</td></tr>
<tr><th scope="row">檢查時</th><td>重複音和長音</td><td>同音高的音可能合併；時值會為了易讀而簡化</td></tr>
<tr><th scope="row">檢查時</th><td>調號</td><td>它是根據音符估算的，可能差一個五度</td></tr>
<tr><th scope="row">匯出前</th><td>最後再移調</td><td>錄音始終保持原調</td></tr>
</tbody>
</table>
</div>

<h2 id="what-to-expect-first-draft">初稿會是什麼樣子？</h2>
<p>你拿到的是一份需要修改的初稿，不是可以直接拿去演奏的成品。我們還沒有測過人聲，所以下面這些來自「單一樂器或人聲」選項的運作方式，以及我們在公開資料集 GuitarSet 的 12 段吉他獨奏和伴奏錄音上的測量，測量分別在 2026年10月2日和 10月4日進行。</p>
<ul>
<li>「單一樂器或人聲」會依偵測到的或你填寫的速度，從 4/4 拍開始按均分的網格記譜，不會推斷三連音和搖擺。請檢查節奏，必要時在「樂譜設定」裡修改拍號。「鋼琴獨奏」能跟上 3/4、4/4、6/8 和 12/8 拍，但它是給鋼琴用的。</li>
<li>音符的結尾沒有開頭可靠。10月2日在這些吉他錄音上，Basic Pitch 的宏平均起音 F1（onset F1）為 0.744；如果連每個音的結尾也要對上，就只有 0.508。10月4日，我們開始先校正走音錄音的音準、把旋律按一次一個音來寫，之後重跑的結果為 0.763 和 0.522。起音 F1 的計算方式是：音高一致、起點誤差在 50 毫秒以內，就算找到了這個音，再把漏掉的音和多出來的音放在一起權衡；1.0 為滿分。具體方法請見《<a href="https://scorestarling.com/zh-hant/blog/ai-music-transcription-accuracy">AI 扒譜到底有多準？我們實測了一下</a>》。</li>
<li>走音的錄音會先校正。如果一段錄音整體比 A440 穩定地偏高或偏低 10 音分以上，轉譜前會先校正音準，讓音符按標準音高記譜；音高飄忽不定的人聲則保持原樣。</li>
<li>寫出來的時值會為了易讀而簡化。在這些吉他錄音中，MuseScore Studio 把 26% 的音符寫得比實際演奏的長（以對齊網格之後為準），最多長出三個十六分音符，從來沒有寫短過。播放和匯出的 MIDI 會保留你原本的節奏；如果希望譜面準確反映你唱的長度，就手動修改音符的時值。</li>
<li>重複音要留意。同一音高的兩個音之間沒有間隙時，模型很難把它們分開，所以要檢查有沒有把你唱的兩個音記成了一個長音。修正方法：先把這個音縮短，再用同樣的音高填上空出來的休止符。</li>
<li>調是猜出來的。它根據音符來估算；在那組吉他錄音裡，12 段中有 2 段猜錯了，兩次都差了一個五度。請在「調號與譜號」裡設成正確的調。</li>
<li>檢查旋律線。偵測到的單音旋律會作為 Melody 聲部寫在一行譜表上：同時奏響的幾個音只保留最強的一個，並去掉微弱的回音；而演奏和弦的聲部會把和弦保留在同一行譜表上。對於單聲部錄音，也可以在「樂譜設定 → 版面」裡為較早的樂譜套用「單行旋律」，它會使用現有的音符。多餘的音符會從播放中拿掉，「復原」可以把它們找回來。請確認它保留的是你唱的那些音。</li>
<li>你只會得到音符。ScoreStarling 不會根據錄音寫出歌詞或和弦標記。</li>
</ul>

<h2 id="write-a-melody-down-by-ear">不借助工具，怎樣憑耳朵把旋律記下來？</h2>
<p>如果沒有合適的工具，你也可以自己記譜：準備一個鍵盤（手機上的鍵盤 App 也行）和一個能重複播放片段的播放器。這樣慢一些，但能練耳朵。</p>
<ol>
<li>找到「家」。哼出旋律最想落腳的那個音，在鍵盤上找到它。它通常就是這個調的主音，能提示你先試哪個調號。</li>
<li>找拍子。跟著錄音用腳打拍子並數拍。每三拍出現一個強拍，可能是 3/4 拍；每四拍一個強拍，可能是 4/4 拍；如果每一拍都分成三份，可以考慮 6/8 拍。</li>
<li>先記節奏，一次一小節，在數拍的位置下面做記號。</li>
<li>再填音高。從一個你確定的音開始，判斷下一個音是級進、小跳還是大跳，然後在鍵盤上核對。</li>
<li>把你記的版本和錄音對照著播放，跟著一起唱。聽起來不對勁的地方，就是要改的音。</li>
</ol>
<p>也有其他 App 能辨識歌聲。ScoreCloud 的 iPhone 和 iPad App「Express」能把唱歌、口哨或哼唱記成樂譜；Klangio 的 Sing2Notes 可以在瀏覽器以及 iOS 和 Android 上使用，支援下載 PDF、MIDI 和 MusicXML（兩者都依據其官網介紹，2026年10月3日查閱）。這兩款我們都沒有測試過。</p>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://support.apple.com/guide/iphone/make-a-recording-iph4d2a39a3b/ios">Make a recording in Voice Memos on iPhone</a>——Apple 支援，《iPhone 使用手冊》（iOS 27）</li>
<li><a href="https://support.apple.com/guide/iphone/export-a-recording-to-files-iph831c37815/ios">Export a Voice Memos recording to Files on iPhone</a>——Apple 支援</li>
<li><a href="https://support.apple.com/guide/iphone/add-a-second-layer-to-a-recording-iphfefa3252b/ios">Add a second layer to a Voice Memos recording on iPhone</a>——Apple 支援</li>
<li><a href="https://support.apple.com/guide/iphone/edit-or-delete-a-recording-iphc9bdaee83/ios">Edit or delete a recording in Voice Memos on iPhone</a>——Apple 支援</li>
<li><a href="https://support.google.com/pixelphone/answer/16267696?hl=en">Save &amp; share recordings &amp; transcripts</a>——Pixel 手機說明，Google</li>
<li><a href="https://support.google.com/pixelphone/answer/16267367?hl=en">Create, edit &amp; delete a recording on your Pixel device</a>——Pixel 手機說明，Google</li>
<li><a href="https://basicpitch.spotify.com/about">About Basic Pitch</a>——Spotify</li>
<li><a href="https://scorecloud.com/">ScoreCloud</a>——ScoreCloud</li>
<li><a href="https://klang.io/sing2notes/">Sing2Notes</a>——Klangio</li>
</ol>
</section><h2>常見問題</h2><h3>可以哼唱嗎？還是得把歌詞唱出來？</h3><p>兩種方式都能給軟體一條有音高的旋律去跟，不過我們都還沒有測過。建議用子音開頭的音節來唱，例如「da」，這樣每個音都有清楚的起點。同一個音高連續重複時，這一點最重要。</p><h3>它能自動算出速度嗎？</h3><p>可以。把「每分鐘拍數」留在「自動」，系統就會偵測速度。不過直接填寫你錄音時用的 BPM 更可靠：我們在 2026年10月3日用 14 段測試錄音檢查過，其中 6 段偵測出的速度誤差在 4% 以內；沒測準的大多偏差 3:2 或 4:3，這是搖擺或三連音律動的典型情況。單一樂器或人聲先按 4/4 拍、均分的節奏網格記譜；如果曲子是別的拍子，轉譜後在「樂譜設定」裡修改。</p><h3>能幫旋律加上歌詞或和弦標記嗎？</h3><p>無法從錄音自動產生。轉譜結果只有音符，沒有歌詞或和弦名稱。你可以解鎖樂譜（一次性 15 點），匯出 MusicXML，再到 MuseScore Studio 等打譜軟體裡加上歌詞或和弦標記。</p><h3>語音備忘錄最長可以錄多久？</h3><p>最長 5 分鐘。更長的檔案（最大 100 MiB）也能上傳，但只轉前 5 分鐘；瀏覽器錄音到 4:59 會自動停止。更長的曲子請在小節線處切開，分段轉譜。</p><h3>上傳的錄音會傳送到哪裡？</h3><p>預設的「單一樂器或人聲」會在 ScoreStarling 自己的伺服器上轉譜，專案只有你的帳號看得到。只有選擇「樂團或多種樂器」時，音訊才會傳送給我們的樂團轉譜合作夥伴 Mirelo。儲存和刪除的規則請見<a href="https://scorestarling.com/zh-hant/privacy">隱私權政策</a>。</p>]]></content:encoded>
    </item>
    <item>
      <title>樂譜 MCP 伺服器總覽：各自能做什麼</title>
      <link>https://scorestarling.com/zh-hant/blog/sheet-music-mcp-servers</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/sheet-music-mcp-servers</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>技術</category>
      <description>我們在 2026年10月3日逐一查看了 26 個樂譜 MCP 伺服器，涵蓋錄音轉樂譜、樂譜渲染、操控 MuseScore 和 Dorico、用 music21 做音樂分析等用途。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>2026年10月3日，我們逐一查看了 26 個與樂譜相關的 MCP 伺服器，它們做的是五類不同的事。兩個雲端託管的伺服器能把錄音轉成樂譜：Mirelo 和我們自己的 ScoreStarling；audio2score-mcp 則用開源元件在你的電腦上完成這件事。MuseScore、Dorico 和 Finale 都有能在你電腦上操控軟體的伺服器，其中仍在積極維護的 MuseScore 伺服器是 mcp-musescore 和 mcp-score。其餘的伺服器，有的渲染 AI 助理寫出的樂譜，有的用 music21 等引擎分析音樂，還有的負責辨識與尋找印刷樂譜。</p>
</section>

<h2 id="how-we-checked">我們是怎麼找到並查核這些伺服器的？</h2>
<p>我們在官方 MCP Registry、GitHub、PulseMCP、Glama 和 mcp.so 上搜尋了與樂譜、記譜、MuseScore、music21、MusicXML、ABC、LilyPond、Dorico 和音訊轉 MIDI 相關的伺服器，再逐一打開這裡列出的每個伺服器的 README 或產品頁面。光靠這些目錄找到的不多：在 PulseMCP 上搜尋「sheet music」只有一個伺服器，搜尋「music21」一個也沒有，所以清單大部分來自 GitHub。</p>
<p>表格中的「最近更新」取最新的發行版本（附版本號），沒有發行版本的則取主分支上的最新提交。「本機」表示伺服器在你的電腦上執行，通常透過 stdio 通訊；「ChatGPT」指的是它的開發者模式。我們對每個雲端託管的伺服器送出一個 MCP initialize 請求，也就是任何用戶端送出的第一則訊息，看它是否要求登入。本機伺服器我們沒有安裝或執行，所以它們的功能以作者的描述為準。我們沒有收錄只產生 MIDI 或音訊的伺服器、DAW（編曲軟體）控制器、串流和中繼資料伺服器、練習類應用程式、其他專案的複製品，以及 README 中沒有描述出可用伺服器的程式碼儲存庫。ScoreStarling 是我們自己的產品，所以它也依同樣的欄位列出，連同它的限制。</p>

<h2 id="recording-to-notation">哪些 MCP 伺服器能把錄音轉成樂譜？</h2>
<p>在我們查看的伺服器中，有三個能接收錄音並回傳樂譜：Mirelo 和 ScoreStarling 是雲端託管，audio2score-mcp 在本機執行。另外三個只做到一半，不產生樂譜：audiolla 和一個以 MuScriptor 為基礎的伺服器回傳 MIDI，fingerstyle-tab-mcp 輸出文字格式的吉他六線譜（TAB）。</p>
<div class="table-scroll" role="region" aria-label="能把錄音轉成樂譜或 MIDI 的 MCP 伺服器" tabindex="0">
<table>
<caption>錄音轉樂譜或 MIDI（2026年10月3日查核）</caption>
<thead>
<tr><th scope="col">伺服器</th><th scope="col">功能</th><th scope="col">執行方式</th><th scope="col">登入</th><th scope="col">寫明的用戶端</th><th scope="col">最近更新</th></tr>
</thead>
<tbody>
<tr><th scope="row"><a href="https://scorestarling.com/zh-hant/mcp">ScoreStarling</a>（我們自己的）</th><td>把錄音、樂譜或記譜文字轉成可編輯的樂譜；可修改、預覽，支援五線譜／六線譜／簡譜，可匯出樂譜和音訊檔案。單一樂器或人聲、鋼琴獨奏免費；樂團需要點數；錄音只轉前五分鐘；檔案最大 100 MiB；未指定速度時自動偵測。<a href="https://scorestarling.com/zh-hant/how-it-works#specs">目前的限制</a>；<a href="https://scorestarling.com/zh-hant/mcp#webmcp">瀏覽器端的 WebMCP Beta</a> 另外提供</td><td>雲端託管</td><td>OAuth；免費帳號</td><td>Claude、ChatGPT、Claude Code、Codex、Cursor、VS Code、Gemini CLI、Devin Desktop、Hermes</td><td>2026年10月8日更新</td></tr>
<tr><th scope="row"><a href="https://mirelo.ai/mcp">Mirelo MCP</a></th><td>對錄音做音訊轉 MIDI，回傳 MIDI 和 MusicXML，自動偵測速度；其餘多半是音效工具</td><td>雲端託管</td><td>OAuth</td><td>Claude、ChatGPT、Cursor、Claude Code、Codex</td><td>2026年7月30日上線</td></tr>
<tr><th scope="row"><a href="https://github.com/David7ce/audio2score-mcp">audio2score-mcp</a></th><td>先用 Basic Pitch 轉成 MIDI，再用 music21 轉成 MusicXML 檔案</td><td>本機</td><td>無</td><td>Claude Code</td><td>2026年8月20日</td></tr>
<tr><th scope="row"><a href="https://github.com/psyb0t/docker-audiolla">audiolla</a></th><td>用 Basic Pitch 做音訊轉 MIDI，是數十個音訊工具之一；不產生樂譜</td><td>自行架設，HTTP</td><td>選用權杖</td><td>不限</td><td>v1.1.7，2026年8月1日</td></tr>
<tr><th scope="row"><a href="https://github.com/ALLLLLL703/midi">midi</a>（MuScriptor）</th><td>用 MuScriptor 模型把音訊轉成多樂器 MIDI；不產生樂譜</td><td>本機，Linux</td><td>無</td><td>未寫明</td><td>2026年8月4日</td></tr>
<tr><th scope="row"><a href="https://github.com/blooper20/fingerstyle-tab-mcp">fingerstyle-tab-mcp</a></th><td>用 Demucs 和 Basic Pitch 把吉他錄音轉成文字六線譜</td><td>本機</td><td>無</td><td>Claude Desktop</td><td>2026年1月15日</td></tr>
</tbody>
</table>
</div>
<p>這兩個雲端託管的伺服器定位不同。Mirelo 的伺服器大部分是音效工具；它的 Audio-to-MIDI 工具回傳檔案，未指定速度時會自動偵測，而它外掛程式的 README 中沒有列出任何編輯樂譜的工具。ScoreStarling 同樣會在你未指定速度時自動偵測，並把樂譜存在你的帳號裡，你可以在那裡改錯音、先試聽修改再套用、移調和匯出；它目前正在等待 ChatGPT 和 Claude 應用程式目錄的審核。Mirelo 也是 ScoreStarling 裡樂團轉譜的合作夥伴。audio2score-mcp 刻意只輸出檔案，把編輯留給你的打譜軟體。MuScriptor 伺服器需要登入 Hugging Face 才能下載模型，而模型權重僅限非商業用途。</p>

<h2 id="render-or-generate-notation">哪些 MCP 伺服器能渲染或產生樂譜？</h2>
<p>有五個伺服器能把 AI 助理以 ABC、LilyPond、JSON 或 MusicXML 寫出的樂譜，變成你能看、能聽或能列印的東西。它們都不能把錄音轉成樂譜。</p>
<div class="table-scroll" role="region" aria-label="能渲染或產生樂譜的 MCP 伺服器" tabindex="0">
<table>
<caption>渲染或產生樂譜（2026年10月3日查核）</caption>
<thead>
<tr><th scope="col">伺服器</th><th scope="col">功能</th><th scope="col">執行方式</th><th scope="col">登入</th><th scope="col">寫明的用戶端</th><th scope="col">最近更新</th></tr>
</thead>
<tbody>
<tr><th scope="row"><a href="https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/sheet-music-server">Sheet Music Server</a>（MCP Apps 範例）</th><td>用 abcjs 把 ABC 記譜轉成可播放的樂譜；只有一個工具</td><td>本機，或使用雲端示範版</td><td>無</td><td>支援 MCP Apps 的宿主應用程式</td><td>v2.0.3，2026年9月25日</td></tr>
<tr><th scope="row"><a href="https://github.com/linxule/mcp-music-studio">MCP Music Studio</a></th><td>把 ABC 轉成樂譜，可播放、移調，可下載 MIDI 和 WAV；支援 Strudel 即時程式設計（live coding）</td><td>雲端託管或本機</td><td>無</td><td>Claude、Claude Code、Codex CLI、Gemini CLI、Cursor、VS Code、Windsurf</td><td>v0.9.2，2026年10月2日</td></tr>
<tr><th scope="row"><a href="https://github.com/delmas41/gradusnotation">Gradus Notation</a></th><td>把 JSON 樂譜轉成 SVG、MusicXML 和 MIDI；和聲分析；排版檢查</td><td>本機，呼叫 Gradus 的 API</td><td>無</td><td>Claude Code、Claude Desktop</td><td>v0.8.0，2026年8月26日</td></tr>
<tr><th scope="row"><a href="https://github.com/wspringer/lilypond-mcp">lilypond-mcp</a></th><td>把 LilyPond 程式碼轉成裁切好的 PDF、EPS、SVG 或 PNG</td><td>本機</td><td>無</td><td>Claude Code、Claude Desktop</td><td>v0.2.2，2026年9月9日</td></tr>
<tr><th scope="row"><a href="https://github.com/raulkivi/sheet-music-mcp">sheet-music-mcp</a></th><td>為合唱團打造的六個伺服器：照片轉 MusicXML、渲染、聲部播放、ABC 編輯、版本差異比對、跟隨演唱者</td><td>本機</td><td>無</td><td>Claude Desktop、Claude Code、Cursor、Windsurf、Continue、Zed</td><td>v0.2.1，2026年9月26日</td></tr>
</tbody>
</table>
</div>
<p>官方範例最精簡：它只有一個工具 <code>play-sheet-music</code>，接收 ABC 並在支援 MCP Apps 的宿主應用程式裡繪製出來；部署在 <code>example-server.modelcontextprotocol.io</code> 的一份副本不需登入就回應了我們的交握請求，Music Studio 的雲端位址也一樣。Gradus 是一個本機套件，會把樂譜傳送到開發者架設的 API（不需要金鑰），並要求 AI 代理在展示輸出時註明出自 Gradus；它還能分析和聲，並對照一本註明出處的規則書檢查排版。lilypond-mcp 執行的是 WebAssembly 版的 LilyPond；raulkivi 的渲染伺服器用的是 Verovio，也就是我們自己也在用的排版引擎。</p>

<h2 id="musescore-dorico-finale">MuseScore、Dorico 或 Finale 有 MCP 伺服器嗎？</h2>
<p>有。我們找到了五個 MuseScore 的、兩個 Dorico 的和一個 Finale 的，都是 GitHub 上的獨立專案，和軟體一起在你的電腦上執行。</p>
<div class="table-scroll" role="region" aria-label="能操控 MuseScore、Dorico 或 Finale 的 MCP 伺服器" tabindex="0">
<table>
<caption>操控打譜軟體（2026年10月3日查核）</caption>
<thead>
<tr><th scope="col">伺服器</th><th scope="col">功能</th><th scope="col">執行方式</th><th scope="col">登入</th><th scope="col">寫明的用戶端</th><th scope="col">最近更新</th></tr>
</thead>
<tbody>
<tr><th scope="row"><a href="https://github.com/ghchen99/mcp-musescore">mcp-musescore</a></th><td>操控在 MuseScore 3 或 4 中開啟的樂譜：音符、休止符、連音、歌詞、小節</td><td>本機，需搭配 MuseScore 外掛程式</td><td>無</td><td>Claude Desktop</td><td>2026年10月2日</td></tr>
<tr><th scope="row"><a href="https://github.com/tskovlund/mcp-score">mcp-score</a></th><td>透過 music21 寫出 MusicXML；即時編輯 MuseScore Studio 4.4.2 以上版本中開啟的樂譜；渲染檔案；對 Dorico 的支援仍屬實驗性質</td><td>本機，需搭配 MuseScore 外掛程式</td><td>無</td><td>Claude Code、Claude Desktop、LM Studio</td><td>v0.1.0，2026年9月25日</td></tr>
<tr><th scope="row"><a href="https://github.com/JordanSucher/musescore-mcp">musescore-mcp</a>（JordanSucher）</th><td>在開啟的 MuseScore 樂譜中輸入基本的音符和休止符</td><td>本機，需搭配 MuseScore 外掛程式</td><td>無</td><td>Claude Desktop</td><td>2025年4月20日</td></tr>
<tr><th scope="row"><a href="https://github.com/strongbeen04/MUSESCORE-MCP">musescore-mcp</a>（strongbeen04）</th><td>包裝 MuseScore 命令列：格式轉換、匯出分譜、移調、把 MIDI 轉成 MuseScore 檔案</td><td>本機</td><td>無</td><td>Claude Desktop、Claude Code</td><td>2026年5月10日</td></tr>
<tr><th scope="row"><a href="https://github.com/achou666666-code/ScoreBridge">ScoreBridge</a></th><td>由 AI 助理辨識 PDF 或圖片裡的印刷樂譜，再轉成可編輯的 MuseScore 檔案</td><td>本機</td><td>無</td><td>未寫明</td><td>v0.1.0，2026年10月1日</td></tr>
<tr><th scope="row"><a href="https://github.com/happycastle114/dorico-mcp-server">dorico-mcp-server</a></th><td>在 Dorico 中輸入音符，並提供記譜、和聲與配器工具</td><td>本機</td><td>在 Dorico 設定中允許</td><td>Claude Desktop、ChatGPT Desktop</td><td>v0.3.0，2026年1月6日</td></tr>
<tr><th scope="row"><a href="https://github.com/romanstark/dorico-maestro">Dorico Maestro</a></th><td>在 Dorico 4 到 6 中於游標處輸入音符、處理 MusicXML 樂譜、檢查聲部進行、匯出 PDF</td><td>本機</td><td>在 Dorico 跳出的提示中確認</td><td>Claude Desktop、Cursor、Antigravity</td><td>v0.1.6，2026年9月10日</td></tr>
<tr><th scope="row"><a href="https://github.com/The-INTJ/FinaleMCP">FinaleMCP</a></th><td>在開啟的 Finale 文件中排版和調整格式，每一步都能復原</td><td>本機，需搭配 Lua 橋接</td><td>無</td><td>Claude Code、Claude Desktop</td><td>2026年7月24日</td></tr>
</tbody>
</table>
</div>
<p>它們都透過各軟體自己的介面運作：MuseScore 的外掛程式、擴充功能宿主或命令列；Dorico 的 Remote Control API，需要你在 Dorico 裡允許，可以在偏好設定中開啟，也可以在第一次連線時跳出的提示中確認；或是 Finale 裡的 RGP Lua 腳本。FinaleMCP 的 README 提到，Finale 已於 2024年8月停止開發和銷售，但仍能執行。它們都不能匯入音訊。JordanSucher 的伺服器仍出現在各個目錄中，但自 2025年4月以來就沒再更新，所以我們會先試 mcp-musescore 和 mcp-score；mcp-score 自稱還在 alpha 階段。官方 MCP Registry 中唯一名稱帶有「musescore」的項目 <a href="https://www.npmjs.com/package/@chrischall/musescore-mcp">@chrischall/musescore-mcp</a>，是一個給 Claude 用的本機套件，它透過你已登入的瀏覽器搜尋 musescore.com 網站，而不是編輯樂譜；它最後一次發布是在 2026年9月28日，我們查看時它的 GitHub 儲存庫回傳了 404。</p>

<h2 id="music21-analysis">有用來做音樂分析的 music21 MCP 伺服器嗎？</h2>
<p>有好幾個。用 music21 自己說明文件裡的話來說，它是「一套以 Python 為基礎、用於電腦輔助音樂學研究的工具套件」；這些伺服器用它或類似的引擎，回答 MIDI、MusicXML 或 MEI 檔案中關於調性、和弦、終止式和聲部進行的問題。</p>
<div class="table-scroll" role="region" aria-label="用於音樂分析的 MCP 伺服器" tabindex="0">
<table>
<caption>音樂分析（2026年10月3日查核）</caption>
<thead>
<tr><th scope="col">伺服器</th><th scope="col">功能</th><th scope="col">執行方式</th><th scope="col">登入</th><th scope="col">寫明的用戶端</th><th scope="col">最近更新</th></tr>
</thead>
<tbody>
<tr><th scope="row"><a href="https://github.com/brightlikethelight/music21-mcp-server">music21-mcp-server</a></th><td>13 個工具：調性、羅馬數字級數、聲部進行、音型模式、配和聲、對位</td><td>本機</td><td>無</td><td>Claude Desktop</td><td>v1.0.0，2026年3月18日</td></tr>
<tr><th scope="row"><a href="https://github.com/SimonsonM/music21-mcp">music21-mcp</a>（SimonsonM）</th><td>七個工具：調性、和弦進行、終止式、配和聲、對位、MIDI</td><td>本機</td><td>無</td><td>Claude Desktop、Claude Code</td><td>2026年4月18日</td></tr>
<tr><th scope="row"><a href="https://github.com/cclawton/music21-mcp">music21-mcp</a>（cclawton）</th><td>16 個工具，用來讀取、編輯和分析 MIDI 檔案</td><td>本機</td><td>無</td><td>未寫明</td><td>2026年7月14日</td></tr>
<tr><th scope="row"><a href="https://github.com/unimelbmdap/encoding-music-mcp">encoding-music-mcp</a></th><td>分析 MEI 檔案（內含 46 份），並用 Verovio 渲染</td><td>本機</td><td>無</td><td>Claude Desktop</td><td>2026年8月31日</td></tr>
<tr><th scope="row"><a href="https://github.com/BluesPrince/thiri-mcp">THIRI Chord Intelligence</a></th><td>和弦分析、和弦配置（voicing）與重新配和聲；非商業授權</td><td>雲端託管或本機</td><td>API 金鑰</td><td>Claude、Cursor</td><td>v0.5.3，2026年8月9日</td></tr>
</tbody>
</table>
</div>
<p>星數最多的 music21-mcp-server 需要從原始碼安裝：我們查看時，它 README 中寫的 pip 套件在 PyPI 上並不存在，主分支最後一次變更是在 2026年3月19日。除了 MCP 伺服器，它還提供一個本機 HTTP API。encoding-music-mcp 出自墨爾本大學的資料分析平台，適合處理學術校訂版的 MEI 樂譜。THIRI 回答的是和弦問題，而不是讀取整份樂譜。</p>

<h2 id="other-notable">還有哪些值得認識的樂譜 MCP 伺服器？</h2>
<p>和扒譜相鄰的還有兩類工作：把印刷樂譜辨識成數位樂譜，以及尋找公共領域的樂譜。</p>
<div class="table-scroll" role="region" aria-label="用於辨識與尋找印刷樂譜的 MCP 伺服器" tabindex="0">
<table>
<caption>辨識與尋找印刷樂譜（2026年10月3日查核）</caption>
<thead>
<tr><th scope="col">伺服器</th><th scope="col">功能</th><th scope="col">執行方式</th><th scope="col">登入</th><th scope="col">寫明的用戶端</th><th scope="col">最近更新</th></tr>
</thead>
<tbody>
<tr><th scope="row"><a href="https://melogenai.com/mcp">Melogen</a></th><td>把印刷樂譜（PDF、PNG、JPG）轉成 MIDI 和 MusicXML；分析歌曲的速度、調性和結構</td><td>雲端託管</td><td>OAuth</td><td>ChatGPT</td><td>未註明日期</td></tr>
<tr><th scope="row"><a href="https://github.com/smeet666/mcp-imslp">mcp-imslp</a></th><td>搜尋 IMSLP 上公共領域的作品、版本和作曲家，並提供連結</td><td>本機</td><td>無</td><td>Claude Code、Claude Desktop、Cursor、VS Code</td><td>v1.0.1，2026年8月29日</td></tr>
</tbody>
</table>
</div>
<p>如果想在自己的電腦上辨識印刷樂譜，可以用上面 raulkivi 那組伺服器裡的 omr-mcp，它能把照片或掃描檔轉成 MusicXML；MuseScore 表格裡的 ScoreBridge 則讓 AI 助理讀譜，再透過 MuseScore 寫出來。它們處理的都是頁面影像，而不是音訊。</p>

<h2 id="how-to-choose">怎麼挑選樂譜 MCP 伺服器？</h2>
<p>先看你要做什麼，再看伺服器在哪裡執行。</p>
<ul>
<li>想在聊天 App 裡把錄音變成可編輯的樂譜，就用雲端託管的錄音轉樂譜伺服器：ScoreStarling 註冊免費帳號就能用，Mirelo 則需要 Mirelo 帳號。比較一下它們怎麼設定速度、一段錄音最長能多久，以及會回傳哪些檔案。</li>
<li>想繼續在 MuseScore、Dorico 或 Finale 裡工作，就用對應軟體的伺服器。它們需要軟體裝在同一台電腦上，通常還要保持開啟，並且需要一個能執行本機伺服器的用戶端。</li>
<li>做分析或教學，可以試試 music21 伺服器或 Gradus；處理 MEI 版本，就用 encoding-music-mcp。</li>
<li>想讓 AI 助理寫譜並展示出來：ABC 可以用 Music Studio 或 Sheet Music Server 範例，LilyPond 可以用 lilypond-mcp。</li>
</ul>
<p>雲端託管的伺服器可以新增到 ChatGPT，或網頁版和手機上的 Claude，但你的檔案會傳送給服務供應商，而且大多需要在對方那裡註冊帳號。ChatGPT 的開發者模式支援 OAuth 或不需驗證的連線，而 Claude 的自訂連接器表單還能填寫固定的請求標頭，例如 API 金鑰（兩者皆於 2026年10月3日查閱）。本機伺服器把檔案留在你的電腦上，需要 Python 或 Node，以及 Claude Desktop、Claude Code、Cursor 或 VS Code 這類桌面用戶端。無論選哪一種，都要看看「最近更新」欄：這裡有四個 GitHub 專案，在我們查看前的六個月裡，主分支沒有任何提交。</p>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://registry.modelcontextprotocol.io/v0/servers?search=musescore">Official MCP Registry API</a>——Model Context Protocol</li>
<li><a href="https://www.pulsemcp.com/servers">PulseMCP server directory</a>——PulseMCP；<a href="https://glama.ai/mcp/servers">MCP servers</a>——Glama；<a href="https://mcp.so/">MCP server directory</a>——mcp.so</li>
<li><a href="https://developers.openai.com/api/docs/guides/developer-mode">ChatGPT Developer mode</a>——OpenAI</li>
<li><a href="https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp">Get started with custom connectors using remote MCP</a>——Claude 說明中心</li>
<li><a href="https://www.music21.org/music21docs/about/what.html">What is music21?</a>——music21 說明文件</li>
<li><a href="https://mirelo.ai/mcp">Mirelo MCP</a>、<a href="https://mirelo.ai/blog/introducing-mirelo-mcp">發表文章</a>和<a href="https://github.com/mirelo-ai/mirelo-plugins">外掛程式</a>——Mirelo</li>
<li><a href="https://melogenai.com/mcp">Melogen Music Intelligence for ChatGPT and MCP</a>——Melogen AI</li>
<li><a href="https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/sheet-music-server">Example: Sheet Music Server</a>——modelcontextprotocol/ext-apps，GitHub</li>
<li>GitHub 上的 README：<a href="https://github.com/David7ce/audio2score-mcp">David7ce/audio2score-mcp</a>、<a href="https://github.com/psyb0t/docker-audiolla">psyb0t/docker-audiolla</a>、<a href="https://github.com/ALLLLLL703/midi">ALLLLLL703/midi</a>、<a href="https://github.com/blooper20/fingerstyle-tab-mcp">blooper20/fingerstyle-tab-mcp</a>、<a href="https://github.com/linxule/mcp-music-studio">linxule/mcp-music-studio</a>、<a href="https://github.com/delmas41/gradusnotation">delmas41/gradusnotation</a>、<a href="https://github.com/wspringer/lilypond-mcp">wspringer/lilypond-mcp</a>、<a href="https://github.com/raulkivi/sheet-music-mcp">raulkivi/sheet-music-mcp</a>、<a href="https://github.com/ghchen99/mcp-musescore">ghchen99/mcp-musescore</a>、<a href="https://github.com/tskovlund/mcp-score">tskovlund/mcp-score</a>、<a href="https://github.com/JordanSucher/musescore-mcp">JordanSucher/musescore-mcp</a>、<a href="https://github.com/strongbeen04/MUSESCORE-MCP">strongbeen04/MUSESCORE-MCP</a>、<a href="https://github.com/achou666666-code/ScoreBridge">achou666666-code/ScoreBridge</a>、<a href="https://github.com/happycastle114/dorico-mcp-server">happycastle114/dorico-mcp-server</a>、<a href="https://github.com/romanstark/dorico-maestro">romanstark/dorico-maestro</a>、<a href="https://github.com/The-INTJ/FinaleMCP">The-INTJ/FinaleMCP</a>、<a href="https://github.com/brightlikethelight/music21-mcp-server">brightlikethelight/music21-mcp-server</a>、<a href="https://github.com/SimonsonM/music21-mcp">SimonsonM/music21-mcp</a>、<a href="https://github.com/cclawton/music21-mcp">cclawton/music21-mcp</a>、<a href="https://github.com/unimelbmdap/encoding-music-mcp">unimelbmdap/encoding-music-mcp</a>、<a href="https://github.com/BluesPrince/thiri-mcp">BluesPrince/thiri-mcp</a>、<a href="https://github.com/smeet666/mcp-imslp">smeet666/mcp-imslp</a></li>
<li><a href="https://www.npmjs.com/package/@chrischall/musescore-mcp">@chrischall/musescore-mcp</a>——npm</li>
</ol>
</section><h2>常見問題</h2><h3>哪個 MuseScore MCP 伺服器最受歡迎？</h3><p>依 2026年10月3日的 GitHub 星數，ghchen99 的 <a href="https://github.com/ghchen99/mcp-musescore">mcp-musescore</a> 以 105 顆星領先，其次是 JordanSucher 的 musescore-mcp（29 顆，自 2025年4月以來就沒再更新），再來是 tskovlund 的 mcp-score（26 顆）。星數代表的是關注度而不是品質，所以也要看看最後一次提交的時間，以及各自支援哪些 MuseScore 版本。</p><h3>這些 MCP 伺服器能在 ChatGPT 裡用嗎？</h3><p>網頁版只能用雲端託管的伺服器。ChatGPT 的開發者模式開放給 Plus、Pro、Business、Enterprise 和 Education 帳號，可以透過 SSE 或串流 HTTP 連線到遠端伺服器，驗證方式為 OAuth 或不需驗證（2026年10月3日查閱）。本機的 stdio 伺服器則需要一個能在你電腦上啟動它們的用戶端，例如 Claude Desktop、Claude Code、Cursor 或 VS Code。</p><h3>有沒有能把音訊轉成吉他六線譜的 MCP 伺服器？</h3><p>fingerstyle-tab-mcp 能在你自己的電腦上用 Demucs 和 Basic Pitch，把吉他錄音寫成文字格式的吉他六線譜（TAB），並提供 Claude Desktop 的設定步驟。它最後一次提交是在 2026年1月15日。從 2026年10月8日起，ScoreStarling 也能把吉他、貝斯或烏克麗麗聲部顯示為六線譜，放在五線譜下方或單獨顯示，並以文字格式回傳 TAB。</p><h3>MuseScore、Steinberg 或 MakeMusic 有推出官方 MCP 伺服器嗎？</h3><p>在我們 2026年10月3日找到的伺服器裡沒有。這裡的 MuseScore、Dorico 和 Finale 伺服器都是 GitHub 上的獨立專案，Dorico Maestro 的 README 還寫明它和 Steinberg 沒有關係。</p><h3>使用 MuseScore 的 MCP 伺服器，需要先安裝 MuseScore 嗎？</h3><p>需要。mcp-musescore 要求 MuseScore 3 或 4 正在執行、裝好它的外掛程式，並開啟一份樂譜。mcp-score 做即時編輯時，需要執行 MuseScore Studio 4.4.2 或更新版本；渲染檔案時只需安裝 MuseScore，不必執行。</p>]]></content:encoded>
    </item>
    <item>
      <title>MusicXML、MIDI 和 PDF 有什麼不同？你該用哪一種</title>
      <link>https://scorestarling.com/zh-hant/blog/musicxml-vs-midi-vs-pdf</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/musicxml-vs-midi-vs-pdf</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>指南</category>
      <description>MusicXML 存的是寫出來的樂譜，MIDI 存的是演奏，PDF 存的是印好的頁面。本文說明三種檔案各存了什麼、該匯出哪一種，以及 .mxl 和 .musicxml 有什麼不同。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>樂譜還要繼續編輯，就匯出 MusicXML；音樂要匯入 DAW（編曲軟體）或合成器，就匯出 MIDI；要給人看譜或列印，就匯出 PDF。MusicXML 存的是寫出來的樂譜：音符的音名寫法、調號和拍號、休止符、聲部和歌詞。MIDI 存的是一次演奏（按了哪個鍵、什麼時候按、按多重），所以要把它變回樂譜只能靠猜。PDF 存的是排好的頁面，裡面沒有任何音樂軟體能編輯的內容。</p>
</section>

<h2 id="what-is-musicxml">MusicXML 是什麼？</h2>
<p>MusicXML 是一種以 XML 為基礎的開放樂譜檔案格式，用來在打譜軟體之間交換和歸檔樂譜。最新的定稿版本 MusicXML 4.0 由 W3C 音樂記譜社群小組（Music Notation Community Group）於 2021年6月1日發布，性質是社群小組的最終報告，和正式的 W3C 標準不是同一回事。2026年8月，該小組表示正在推進 4.1 版。</p>
<p>它按樂譜的寫法來描述音樂。音高由音名、升降變化和八度三部分組成，所以 D♯ 和 E♭ 始終是兩個不同的音。每個音符既有時值（以四分音符的細分單位計數），也有記譜上的類型，例如附點八分音符。休止符、聲部編號、符槓、符桿方向、調號（以五度圈上的位置表示：正數代表升記號，負數代表降記號）、拍號、附段號的歌詞，以及選用的頁面和譜行排版，都有各自的元素。這種格式也把聲音和外觀分開：速度可以按每分鐘幾個四分音符來存，音符也可以帶上細微的起音和釋放偏移，這些屬於演奏，而不屬於譜面。</p>
<p>打譜軟體靠它互換樂譜。MuseScore Studio、Dorico 和 Sibelius 的說明文件都寫明支援匯入 MusicXML；MakeMusic 的 MusicXML 網站列出超過 260 款支援此格式的應用程式（2026年10月3日查閱）。</p>

<h2 id="what-is-a-midi-file">MIDI 檔案是什麼？它少了什麼？</h2>
<p>MIDI 檔案是一份依時間排列的演奏事件清單：哪個鍵在什麼時刻、以多大力度按下，又在什麼時刻放開，該用哪種樂器音色發聲。MIDI 1.0 誕生於 1983年。它的檔案格式「標準 MIDI 檔案 1.0」（RP-001，最近一次修訂是 1996年2月），就是為了讓軟體之間能交換帶時間戳記的 MIDI 資料。</p>
<p>MIDI 檔案還能用 meta 事件記下幾項音樂資訊：速度（以每個四分音符幾微秒來表示）、拍號、調號（以升記號或降記號的數量，加上大調或小調來表示），以及歌詞，通常一個音節一個事件。規格要求每個檔案寫明速度和拍號；沒寫的話，軟體就預設以 4/4 拍、每分鐘 120 拍處理。調號和歌詞則是選用的。</p>
<p>MIDI 說不清楚的，是音符該怎麼寫。一個音符只是 0 到 127 之間的一個鍵號，所以 63 號既是 D♯ 也是 E♭；我們做的第一個轉換器把 MIDI 轉成樂譜時，每次都把 63 寫成 E♭，連在本該寫 D♯ 的 E 大調裡也一樣。音的長短只記成開始和結束時間，所以音符時值只能用推斷的。沒有休止符（靜音只是一段空白），沒有聲部、符槓和符桿，也沒有頁面排版。MuseScore Studio 的手冊說，MIDI 記錄了音符和樂器，「但不含任何視覺格式」。MIDI 裡也沒有音訊：聲音來自播放它的合成器。</p>

<h2 id="musicxml-vs-midi-vs-pdf-comparison">MusicXML、MIDI 和 PDF 比較：各自存了什麼</h2>
<p>MusicXML 存的是「怎麼寫」，MIDI 存的是「怎麼彈」，PDF 存的是「頁面長什麼樣子」。PDF 是通用的頁面格式，標準編號為 ISO 32000；PDF 2.0（ISO 32000-2）於 2017年首次發布、2020年修訂，美國國會圖書館的數位格式資料庫稱它是一種「以頁面為導向」的格式。PDF 樂譜在任何螢幕或印表機上看起來都一模一樣，但在音樂軟體眼中，它只是一張畫著音符的圖片。</p>
<div class="table-scroll" role="region" aria-label="MusicXML、MIDI 和 PDF 檔案各存了什麼" tabindex="0">
<table>
<caption>各檔案存了什麼</caption>
<thead><tr><th scope="col">資訊</th><th scope="col">MusicXML</th><th scope="col">MIDI 檔案</th><th scope="col">PDF</th></tr></thead>
<tbody>
<tr><th scope="row">音名寫法（D♯ 還是 E♭）</th><td>有：音名、升降變化、八度</td><td>無：只有 0 到 127 的鍵號</td><td>僅印在譜面上</td></tr>
<tr><th scope="row">調號</th><td>有</td><td>選用：升降記號的數量，以及大調或小調</td><td>僅印在譜面上</td></tr>
<tr><th scope="row">拍號</th><td>有</td><td>建議寫入；沒寫時預設為 4/4 拍</td><td>僅印在譜面上</td></tr>
<tr><th scope="row">音符時值和休止符</th><td>有</td><td>無：只有開始和結束時間</td><td>僅印在譜面上</td></tr>
<tr><th scope="row">聲部、符槓、符桿</th><td>有</td><td>無</td><td>僅印在譜面上</td></tr>
<tr><th scope="row">歌詞</th><td>有，附歌詞段號</td><td>選用的歌詞事件，通常一個音節一個</td><td>僅印在譜面上</td></tr>
<tr><th scope="row">頁面排版</th><td>選用：頁面尺寸、邊界、換行和換頁</td><td>無</td><td>有，固定不變</td></tr>
<tr><th scope="row">速度</th><td>有，以每分鐘四分音符數計</td><td>有，以每個四分音符的微秒數計；沒寫時預設 120 BPM</td><td>只有印在譜面上的速度標記</td></tr>
<tr><th scope="row">精確的時間和演奏力度</th><td>大多沒有：只有書面時值，可另加演奏偏移和力度值</td><td>有，每個音符都有</td><td>無</td></tr>
<tr><th scope="row">吉他的弦和格</th><td>有：每個音的弦和格，以及各弦的調音和移調夾</td><td>無</td><td>僅以六線譜形式印在譜面上</td></tr>
<tr><th scope="row">樂器音色</th><td>樂器名稱和選用的 MIDI 音色編號；不含音訊</td><td>音色編號；不含音訊</td><td>無</td></tr>
<tr><th scope="row">在哪裡編輯</th><td>打譜軟體</td><td>DAW 和音序器；打譜軟體匯入時只能靠猜</td><td>無法當作音樂編輯：只能重新輸入，或用 OMR 辨識</td></tr>
</tbody>
</table>
</div>
<p class="note">「僅印在譜面上」是指人能在頁面上讀到，但軟體沒辦法把它當作音樂處理。要從 PDF 取回樂譜，得用光學樂譜辨識（OMR）讀取影像、推斷音符，所以結果需要校對。</p>

<h2 id="which-format-to-export">該匯出哪種格式？</h2>
<p>看檔案接下來要拿去做什麼。</p>
<ul>
<li>有人要編輯樂譜，就給 MusicXML。MuseScore Studio（免費開源）、Dorico 和 Sibelius 都能匯入。除非某個工具指定要未壓縮的 .musicxml，否則就給 .mxl。</li>
<li>音樂要匯入 DAW 或驅動合成器，就給 MIDI。它保留了每個音符的時間和演奏力度，這正是音樂製作需要的。</li>
<li>有人要照著演奏，就給 PDF，印出來或在平板上看都行；合奏的話，再給每個聲部準備分譜。</li>
<li>對方只需要聽，就給渲染好的 MP3 或 WAV；MIDI 檔案本身不會發出聲音。</li>
<li>要存檔的話，用 MusicXML 留住音樂內容，用 PDF 留住版面樣子。這是我們的習慣，不是硬性規定。</li>
</ul>
<p>Finale 使用者還要多考慮一點。MakeMusic 已於 2024年8月26日停止開發和銷售 Finale。只要作業系統沒有變動，已經裝好的 Finale 還能繼續用；MakeMusic 對它的技術支援已於 2025年8月結束。MakeMusic 官方的移轉建議是匯出 MusicXML（Finale v27 匯出的是 MusicXML 4.0），因為 Dorico 無法直接開啟 Finale 的 .mus 或 .musx 檔案（2026年10月3日查閱）。</p>
<p>想把 MIDI 檔案變成 PDF 樂譜，就把它匯入打譜軟體，在裡面整理好，再匯出 PDF。MuseScore Studio 能開啟 MIDI 檔案，也可以在偏好設定裡指定匯入時使用的最短音符時值；Dorico Pro 的手冊也介紹了它自己的 MIDI 匯入和量化選項。不管用哪一個，都要有整理的心理準備，原因見下文「為什麼 MIDI 轉樂譜總要靠猜」一節。ScoreStarling 現在也接受 MIDI：按「選擇檔案」或直接把檔案拖進來，就能排成可編輯的樂譜，並匯出 PDF。它會略過鼓軌，只採用第一個拍號，排好後仍需要你親自檢查一遍。你也可以匯入 MusicXML/MXL、ABC 或印刷樂譜，詳見<a href="https://scorestarling.com/zh-hant/how-it-works#sheet-music">目前的輸入限制</a>。</p>

<h2 id="mxl-vs-musicxml">MXL 和 MusicXML 有什麼不同？</h2>
<p>兩者裝的是同樣的 MusicXML：.mxl 是壓縮檔，.musicxml 是純文字檔。W3C 的 MusicXML 4.0 教學推薦的正是這兩個副檔名，並指出如果只用 .xml 當副檔名，軟體就無法把 MusicXML 和其他 XML 檔案區分開來。</p>
<p>.mxl 檔案是用 DEFLATE 演算法壓縮的 zip 壓縮檔。壓縮檔裡的第一個項目是一個未壓縮的檔案，名為 mimetype；位於 META-INF/container.xml 的檔案則指出樂譜在壓縮檔裡的位置。根據教學的說法，壓縮後的 MusicXML 大約和同等的 MIDI 檔案一樣大，所以我們會給 .mxl。MuseScore Studio 預設匯出的就是壓縮的 MusicXML。如果你想自己閱讀、比對或處理 XML，就選 .musicxml。兩者的媒體類型也不同：.mxl 是 <code>application/vnd.recordare.musicxml</code>，.musicxml 是 <code>application/vnd.recordare.musicxml+xml</code>。</p>

<h2 id="midi-to-sheet-music-guesswork">為什麼 MIDI 轉樂譜總要靠猜？</h2>
<p>MIDI 檔案從來不說明音符該怎麼寫，所以任何把 MIDI 轉成樂譜的軟體，都得自己決定調性、音名寫法、音符時值、聲部，以及休止符要放在哪裡。</p>
<p>我們實測過它會怎麼出錯。2026年10月2日，我們從公開資料集 GuitarSet 中取了 12 段吉他錄音，用 Basic Pitch 轉出 MIDI，交給我們最早以 music21 函式庫寫的轉換器。結果 12 份樂譜都沒有調號；所有休止符都被隱藏（1,626 個中可見的為 0）；一行譜表上最多疊了五個聲部；在 2,703 個調內音中，有 322 個的寫法與調性不符。</p>
<p>改用 MuseScore Studio 4.7.5 的 MIDI 匯入器後，這些問題大多解決了，前提是先把 MIDI 處理好：把音符對齊到十六分音符網格，再根據音符推估調性，寫進每一條有音符的音軌。少了這一步，MuseScore 會把沒對齊網格的 MIDI 當成現場演奏，重新猜一遍拍號、速度和調。我們還把匯入限制為每行譜表最多兩個聲部，並關閉了連音辨識。在這些設定下，12 份裡有 10 份的調號與資料集標註一致（另外兩份都差一個五度），3,213 個音符裡寫法與調性不符的為 0，988 個休止符全部可見，沒有任何一行譜表超過兩個聲部。完整的前後比較請見《<a href="https://scorestarling.com/zh-hant/blog/why-midi-imports-look-messy">MIDI 匯入 MuseScore 為什麼亂七八糟</a>》。</p>

<h2 id="musicxml-import-differences">為什麼 MusicXML 換個軟體開啟就走樣？</h2>
<p>MusicXML 能把音樂內容完整帶過去，但每個軟體都有自己的字型、間距和預設值，所以匯入後版面和小細節常會走樣。MuseScore Studio 的手冊說，音符和樂器編制都能如實匯入，但要讓匯入的樂譜看起來和原版一樣，通常還得整理一番。它的匯入選項可以保留檔案裡的頁面尺寸、邊界、譜表大小和換行換頁，不過因為間距和字型不同，換行換頁的位置不一定和原本一致。MakeMusic 從 Finale 移轉到 Dorico 的指南也是這麼說：音樂內容，以及頁面和譜表尺寸這類基本版面，都能順利轉過去，但對個別元素做的圖形微調通常帶不過去。</p>
<p>我們在 2026年10月2日自己做過一次往返測試，規模很小：MuseScore Studio 4.7.5 的命令列轉換器重新匯入了我們一份只有三個音符的 MusicXML 範例，音高、位置和時值完全一致，但它把我們的記譜者署名移到了作曲者的位置，還弄丟了「記譜者」這個身分。Sibelius 和 Dorico 我們沒有測試。把樂譜轉給別人之前，先在對方要用的軟體裡開啟，檢查署名、版面，以及你手動調整過的地方。</p>

<h2 id="what-scorestarling-exports">ScoreStarling 匯出哪些檔案，為什麼 MIDI 和 MusicXML 不一樣？</h2>
<p>兩種檔案 ScoreStarling 都能匯出，而且兩者在時間上刻意不一致：MIDI 保留演奏，MusicXML 保留一份寫給人看的版本。</p>
<p>在我們 10月2日針對「單一樂器」選項的測試中，轉譜先由 Basic Pitch 依給定的速度產生 MIDI。MuseScore Studio 再根據一份對齊到十六分音符網格的副本寫出樂譜：4/4 拍，調性由音符推估，休止符可見，每行譜表最多兩個聲部。它還會簡化時值：在上文那 12 段吉他錄音中，它把 26% 的音符寫得比對齊後的演奏更長，最多長出三個十六分音符，從來沒有寫短過。演奏者想看到乾淨俐落的附點四分音符，製作人要的則是原汁原味的律動。所以播放和 MIDI 匯出保留你的演奏時間，而 MusicXML、PDF 和頁面圖片呈現的是記譜版本。改音高會同時改動兩種檔案；你設定某個音符的書面時值時，它對應的 MIDI 音符會以原本彈奏的位置為起點調整長度。其他工具也是這樣區分的：Klangio 的 Sing2Notes 應用程式同時提供量化和未量化兩種 MIDI 下載（2026年10月3日查閱）。</p>
<p>ScoreStarling 免費提供的下載，以及解鎖後才有的下載：</p>
<div class="table-scroll" role="region" aria-label="ScoreStarling 的免費下載和解鎖後下載" tabindex="0">
<table>
<caption>ScoreStarling 可下載的檔案（2026年10月8日）</caption>
<thead><tr><th scope="col">取得方式</th><th scope="col">檔案</th></tr></thead>
<tbody>
<tr><th scope="row">免費</th><td>PDF、頁面圖片、獨立的 PDF 分譜、簡譜 PDF（全部譜表或只有旋律）、含吉他六線譜的 PDF、TAB 文字檔、MP3 和 WAV。樂譜解鎖前，每頁和 TAB 文字檔底部都有一行頁尾小字：「Made with ScoreStarling · scorestarling.com」。</td></tr>
<tr><th scope="row">解鎖後（每份樂譜一次 15 點）</th><td>MusicXML（.musicxml 和 .mxl；顯示為 TAB 譜的聲部會附上 TAB 譜表）、MIDI、ABC 和 MEI，頁面也不再有頁尾小字。用點數轉出的樂團樂譜本身就已解鎖；訂閱 Plus 期間，所有樂譜都會解鎖。</td></tr>
</tbody>
</table>
</div>
<p>工作區的「下載」選單提供的 MusicXML 是 .musicxml 格式；.mxl 和 MEI 要透過 AI 助理取得。單一樂器或人聲、鋼琴獨奏的轉譜（合計每天最多 3 次）以及匯入和編輯都免費；樂團需要點數。簡譜在工作區和對話面板裡也能以唯讀檢視查看，編輯請在五線譜中進行。<a href="https://scorestarling.com/zh-hant/how-it-works#notation">簡譜在保留記譜資訊方面有其限制</a>。MP3 和 WAV 是以 General MIDI 音色渲染的樂譜，不是從你的錄音裡分離出來的分軌。詳情請見<a href="https://scorestarling.com/zh-hant/how-it-works#specs">技術規格</a>。</p>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://www.w3.org/2021/06/musicxml40/">MusicXML 4.0</a> 和 <a href="https://www.w3.org/community/music-notation/">Music Notation Community Group</a>——W3C</li>
<li><a href="https://www.w3.org/2021/06/musicxml40/tutorial/notation-basics/">Notation basics in MusicXML</a> 和 <a href="https://www.w3.org/2021/06/musicxml40/tutorial/midi-compatible-part/">A MIDI-compatible part</a>，MusicXML 4.0 教學——W3C Music Notation Community Group</li>
<li><a href="https://www.w3.org/2021/06/musicxml40/tutorial/compressed-mxl-files/">Compressed .mxl files</a>，MusicXML 4.0 教學——W3C Music Notation Community Group</li>
<li>MusicXML 4.0 參考文件：<a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/note/">note</a>、<a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/fifths/">fifths</a>、<a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/lyric/">lyric</a>、<a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/print/">print</a>、<a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/midi-instrument/">midi-instrument</a>、<a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/string/">string</a>、<a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/fret/">fret</a>、<a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/staff-tuning/">staff-tuning</a> 和 <a href="https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/capo/">capo</a> 元素——W3C Music Notation Community Group</li>
<li><a href="https://midi.org/midi-1-0">MIDI 1.0</a>——The MIDI Association</li>
<li><a href="https://midi.org/summary-of-midi-1-0-messages">Summary of MIDI 1.0 messages</a>——The MIDI Association</li>
<li><a href="https://midi.org/standard-midi-files">Standard MIDI Files</a> 和 <a href="https://midi.org/standard-midi-files-specification">Standard MIDI Files 1.0 specification (RP-001)</a>——The MIDI Association</li>
<li><a href="https://www.loc.gov/preservation/digital/formats/fdd/fdd000474.shtml">PDF 2.0, ISO 32000-2</a>——美國國會圖書館，Sustainability of Digital Formats</li>
<li><a href="https://handbook.musescore.org/file-management/file-export">File export</a>、<a href="https://handbook.musescore.org/file-management/working-with-musicxml-files">Working with MusicXML files</a>、<a href="https://handbook.musescore.org/file-management/opening-and-saving-scores">Opening and saving scores</a> 和 <a href="https://handbook.musescore.org/customization/preferences">Preferences</a>——MuseScore Studio 手冊</li>
<li><a href="https://www.steinberg.help/r/dorico-pro/6.2/en/dorico/topics/project_file_handling/project_file_handling_musicxml_files_importing_t.html">Importing MusicXML files</a> 和 <a href="https://www.steinberg.help/r/dorico-pro/6.2/en/dorico/topics/project_file_handling/project_file_handling_midi_importing_t.html">Importing MIDI</a>，Dorico Pro 6.2 手冊——Steinberg</li>
<li><a href="https://resources.avid.com/SupportFiles/Sibelius/Using_Sibelius_for_mobile_EN.pdf">Using Sibelius for Mobile on iPhone, iPad, and Android</a>——Avid</li>
<li><a href="https://www.finalemusic.com/blog/end-of-finale-new-journey-dorico-letter-from-president/">The End of Finale</a> 和 <a href="https://www.finalemusic.com/blog/from-finale-to-dorico-a-migration-guide/">From Finale to Dorico, a migration guide</a>——MakeMusic</li>
<li><a href="https://www.musicxml.com/">MusicXML for exchanging digital sheet music</a>——MakeMusic</li>
<li><a href="https://klang.io/sing2notes/">Sing2Notes</a>——Klangio</li>
</ol>
</section><h2>常見問題</h2><h3>MIDI 檔案裡有聲音嗎？</h3><p>沒有。它存的只是指令：彈哪個鍵、什麼時候彈、彈多重，外加一個樂器音色編號。聲音來自播放它的合成器或取樣音源，所以同一個 MIDI 檔案在不同軟體裡聽起來不一樣。</p><h3>為什麼我的 MIDI 檔案一打開就是 4/4 拍、120 BPM？</h3><p>標準 MIDI 檔案規格要求每個檔案寫明速度和拍號，並規定檔案沒寫時，軟體一律以 4/4 拍、每分鐘 120 拍處理。先把真實的速度和拍號設好，再判斷樂譜寫得對不對。</p><h3>PDF 能轉回 MusicXML 嗎？</h3><p>只能靠光學樂譜辨識（OMR）：它讀取頁面影像，推斷出譜面內容，所以結果需要校對。如果原始的打譜檔案或它的 MusicXML 還在，就直接從那裡開始。</p><h3>MusicXML 和 MuseScore、Sibelius 的檔案是一回事嗎？</h3><p>不是。MuseScore Studio 的 .mscz 和 Sibelius 的 .sib 都是各自軟體的自家格式，MusicXML 則是軟體之間交換樂譜用的格式。例如 MuseScore Studio 除了自己的格式，也能開啟 MusicXML、壓縮的 MusicXML 和 MIDI 檔案。</p><h3>給合唱團或樂團，該傳哪種檔案？</h3><p>看譜用 PDF；如果每位樂手只需要自己那一行，就給分譜。如果有人要移調、改編或修改分譜，再附上 MusicXML。</p>]]></content:encoded>
    </item>
    <item>
      <title>遠端 MCP 伺服器的 OAuth 實戰：上線經驗總結</title>
      <link>https://scorestarling.com/zh-hant/blog/remote-mcp-server-oauth</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/remote-mcp-server-oauth</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>技術</category>
      <description>我們託管的 MCP 伺服器如何透過 OAuth 2.1 和 Supabase Auth 讓使用者登入：401 挑戰、RFC 9728 中繼資料、用戶端註冊、權杖受眾與權杖更新。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>遠端 MCP 伺服器是一種 OAuth 受保護資源。請求沒帶權杖時，它會回傳 401，並指向自己的 RFC 9728 中繼資料；用戶端順著中繼資料找到授權伺服器、完成註冊，用 PKCE 和 <code>resource</code> 參數讓使用者登入，最後帶著為該伺服器 URL 簽發的 Bearer 權杖回來。我們在正式環境中用 Supabase Auth 執行這套流程。真正花工夫的是三個細節：401 要帶 scope 提示、不帶錯誤碼；用權杖 hook 把我們的受眾和 scope 寫進 Supabase 簽發的權杖；還有動態用戶端註冊，因為我們的授權伺服器不支援用戶端 ID 中繼資料文件。</p>
</section>

<h2 id="mcp-oauth-roles">MCP 伺服器採用 OAuth 時，各方分別負責什麼？</h2>
<p>MCP 伺服器的工作最少：只負責接受或拒絕 Bearer 權杖。MCP 授權規範（2025-11-25 版）把它定為 OAuth 2.1 資源伺服器，把 AI 助理定為 OAuth 用戶端，並允許負責使用者登入和簽發權杖的授權伺服器是獨立的服務。</p>
<p>我們的授權伺服器就是獨立的。所有權杖都由 Supabase Auth 的 OAuth 2.1 伺服器簽發，登入和授權同意頁面由我們的網站提供，<code>https://mcp.scorestarling.com/mcp</code> 這個端點只負責驗證權杖。它以 Streamable HTTP 執行，不使用 MCP 工作階段，每個請求都會驗證權杖。ScoreStarling 能把錄音變成可編輯的樂譜，AI 助理就是透過這個端點使用它；使用者要做的步驟請見<a href="https://scorestarling.com/zh-hant/mcp">連線指南</a>。</p>

<h2 id="mcp-oauth-flow">MCP 用戶端連線時會發生什麼事？</h2>
<p>用戶端一開始只有一個 URL。我們伺服器的兩次回應（以下為 2026年10月3日的實際內容）告訴它去哪裡登入，之後再經過六個步驟取得權杖。首先，沒帶權杖的請求會收到 401，其 <code>WWW-Authenticate</code> 回應標頭寫明了中繼資料的位置和要申請的 scope：</p>
<pre><code>$ curl -si -X POST https://mcp.scorestarling.com/mcp \
    -H 'content-type: application/json' \
    -H 'accept: application/json, text/event-stream' -d '{}'
HTTP/2 401
content-type: application/json
www-authenticate: Bearer resource_metadata="https://mcp.scorestarling.com/.well-known/oauth-protected-resource/mcp", scope="openid email profile"

{"error_description": "Authentication required"}</code></pre>
<p>接著，用戶端取得這份中繼資料。依照 RFC 9728，中繼資料網址是在主機名稱和路徑之間插入 <code>/.well-known/oauth-protected-resource</code> 而成；它還要求 <code>resource</code> 與用戶端正在使用的 URL 完全相同，否則用戶端必須停止。以下已把我們的 Supabase 專案 ID 換成佔位文字：</p>
<pre><code>{
  "resource": "https://mcp.scorestarling.com/mcp",
  "authorization_servers": ["https://&lt;project-ref&gt;.supabase.co/auth/v1"],
  "scopes_supported": ["openid", "email", "profile"],
  "bearer_methods_supported": ["header"]
}</code></pre>
<p>其餘六個步驟：</p>
<ol>
<li><p>用戶端讀取授權伺服器的中繼資料。Supabase 的 issuer 帶有路徑，所以依照 RFC 8414，well-known 區段要放在路徑前面：<code>https://&lt;project-ref&gt;.supabase.co/.well-known/oauth-authorization-server/auth/v1</code>。用戶端會檢查 <code>issuer</code> 是否完全相符，以及 <code>code_challenge_methods_supported</code> 是否列出 <code>S256</code>；MCP 規範規定，缺少這個欄位就必須停止。</p></li>
<li><p>用戶端把自己的中繼資料 POST 到中繼資料所公布的 <code>registration_endpoint</code>（動態用戶端註冊，RFC 7591），取得一個 <code>client_id</code>。我們的測試用戶端以公用用戶端身分註冊，不帶用戶端密碼；Anthropic 的文件說 Claude 也是如此。</p></li>
<li><p>用戶端在瀏覽器中開啟授權端點，帶上 PKCE S256 challenge、401 給出的 scope，以及 <code>resource=https://mcp.scorestarling.com/mcp</code>（RFC 8707）。Supabase 把瀏覽器轉交給我們的授權同意頁面：必要時先讓使用者登入，再請對方核准或拒絕這個用戶端。</p></li>
<li><p>用戶端用 PKCE verifier 和同一個 <code>resource</code> 交換授權碼，取得一個效期一小時的存取權杖和一個更新權杖。</p></li>
<li><p>之後的每個 MCP 請求都帶上 <code>Authorization: Bearer …</code>。我們用 issuer 公布的金鑰驗證簽章，再檢查 issuer、受眾、到期時間和寫入 scope，最後確認帳號沒有被停用。</p></li>
<li><p>權杖到期，或我們回傳帶 <code>invalid_token</code> 的 401 時，用戶端會更新權杖（同樣帶上 <code>resource</code>），取得新的存取權杖和更新權杖。</p></li>
</ol>
<p>即使授權伺服器會忽略 <code>resource</code>，用戶端也必須在授權請求和權杖請求中帶上它。Supabase 的 OAuth 流程文件沒有提到這個參數（2026年10月3日確認）。在我們所有的正式環境實測中，Supabase 都接受了它，而我們權杖裡的受眾來自 hook。</p>

<h2 id="mcp-401-www-authenticate">401 回應的 WWW-Authenticate 標頭該包含什麼？</h2>
<p>請求沒帶權杖時：寫明中繼資料在哪裡、該申請哪些 scope，不帶錯誤碼。RFC 6750 §3.1 指出，完全不帶驗證資訊的請求不應收到錯誤碼。<code>invalid_token</code> 是給帶了權杖但驗證失敗的情況，告訴用戶端可以換個新權杖再重試。MCP 規範還補充，401 應帶有 <code>scope</code> 提示，用戶端必須把它當成要申請的 scope。</p>
<p>我們是在撰寫連線指南時，發現自己的伺服器在這點上做錯了。在 MCP Python SDK 1.30.0 中，<code>RequireAuthMiddleware</code> 對所有未驗證的請求都回傳 <code>error="invalid_token"</code>，而且不帶 scope，結果一個從沒送過權杖的用戶端被告知「權杖無效」。現在我們在 MCP 路由外包了一層小小的 ASGI 中介軟體，只改寫 SDK 回傳的 401；放不放行仍由 SDK 決定，它回傳的 403 <code>insufficient_scope</code> 也原封不動：</p>
<pre><code># No token, before the fix (the SDK's default; wrapped for reading)
WWW-Authenticate: Bearer error="invalid_token",
    error_description="Authentication required",
    resource_metadata="…/.well-known/oauth-protected-resource/mcp"

# No token, now
WWW-Authenticate: Bearer
    resource_metadata="…/.well-known/oauth-protected-resource/mcp",
    scope="openid email profile"

# Rejected token: expired, wrong audience, malformed or account turned off
WWW-Authenticate: Bearer error="invalid_token",
    error_description="Invalid or expired access token",
    resource_metadata="…/.well-known/oauth-protected-resource/mcp",
    scope="openid email profile"</code></pre>
<p>提示中寫的是 <code>openid email profile</code>，也就是 Supabase 能授予的 scope，與 <code>scopes_supported</code> 一致。現在只要沒帶權杖時的挑戰中出現錯誤碼，我們的部署檢查就會失敗。這項修正在 2026年10月2日上線，距離合併不到四分鐘。Anthropic 的連接器文件還為 Claude 補充了兩條規則：登入只會由 401 觸發（Claude 會忽略 200 回應中的 <code>WWW-Authenticate</code>），而且只使用 <code>authorization_servers</code> 中的第一個項目。</p>

<h2 id="supabase-custom-access-token-hook">如何把 MCP 受眾和 scope 寫進 Supabase 的權杖？</h2>
<p>用自訂存取權杖 hook。MCP 規範要求伺服器只接受為自己簽發的權杖，實務上就是要檢查 <code>aud</code>；我們也想要一個自己的寫入 scope，而 Supabase 的文件說它的 OAuth 伺服器不支援自訂 scope（2026年10月3日確認）。這個 hook 是一個 Postgres 函式，Supabase Auth 在簽發權杖前執行它，它可以改寫權杖中的 claims。以下是我們的版本，依遷移檔稍作簡化：</p>
<pre><code>create or replace function scorestarling.access_token_hook(event jsonb)
returns jsonb language plpgsql stable security invoker set search_path = '' as $$
declare claims jsonb := event-&gt;'claims';
begin
  -- Tokens issued to an OAuth client carry its client_id; website sessions don't.
  if coalesce(claims-&gt;&gt;'client_id', '') &lt;&gt; '' then
    claims := jsonb_set(claims, '{aud}', '"https://mcp.scorestarling.com/mcp"');
    claims := jsonb_set(claims, '{scope}', '"openid email profile scorestarling:write"');
  end if;
  return jsonb_build_object('claims', claims);
end;
$$;
grant usage on schema scorestarling to supabase_auth_admin;
grant execute on function scorestarling.access_token_hook(jsonb) to supabase_auth_admin;
revoke all on function scorestarling.access_token_hook(jsonb) from public, anon, authenticated;</code></pre>
<p>MCP 端用 PyJWT 驗證這些 claims，再套用兩條自己的規則：</p>
<pre><code>claims = jwt.decode(token, key, algorithms=["RS256", "ES256"],
                    issuer=ISSUER, audience=ACCEPTED_AUDIENCES,
                    options={"require": ["exp", "iat", "iss", "aud", "sub"]})
if "scorestarling:write" not in claims.get("scope", "").split():
    return None  # the client gets 401 invalid_token
# then: has this account been turned off?</code></pre>
<p><code>ACCEPTED_AUDIENCES</code> 是 MCP 的 URL，搬遷期間再加上舊的 URL。對 client_id 的判斷把網站自己的登入擋在外面：同一個 Supabase 專案簽發的瀏覽器工作階段權杖，issuer 和金鑰都一樣，但沒有 <code>client_id</code>，所以永遠拿不到 MCP 受眾。</p>
<p>這個 hook 有兩個代價。第一，它寫入的是一個固定的受眾，而不是依用戶端傳來的 <code>resource</code>；只要一個授權伺服器只服務一個 MCP 伺服器，這樣就沒問題。第二，我們的授權同意頁面只能顯示用戶端申請的 scope，所以寫入 scope 其實是在使用者核准用戶端時授予的。</p>
<p class="note">有一則尚未關閉的問題回報 <a href="https://github.com/supabase/auth/issues/2820">supabase/auth#2820</a>（2026年9月20日提出，截至 10月3日無人回覆）指出，對公用用戶端、<code>offline_access</code> 或 <code>resource</code>，Supabase 取得授權詳細資料的請求會回傳 400。這些情況我們都無法重現；在我們讀過的 Supabase Auth 原始碼中，只有授權已不在待處理狀態時（例如同一個授權被讀取了兩次），才會出現這個 400。</p>

<h2 id="dcr-vs-cimd">動態用戶端註冊，還是用戶端 ID 中繼資料文件？</h2>
<p>授權伺服器宣告支援哪種就用哪種；目前用 Supabase 的話，就是動態用戶端註冊（DCR）。2025-11-25 版 MCP 規範建議，在用戶端和伺服器素未謀面時使用用戶端 ID 中繼資料文件（CIMD），保留 DCR 則是為了向下相容。用戶端應先嘗試預先提供的用戶端 ID，再來是 CIMD，然後是 DCR，都不行才詢問使用者。</p>
<div class="table-scroll" role="region" aria-label="MCP 用戶端取得用戶端 ID 的幾種方式" tabindex="0">
<table>
<caption>MCP 用戶端如何取得用戶端 ID</caption>
<thead><tr><th scope="col">方式</th><th scope="col">用戶端如何表明身分</th><th scope="col">伺服器中繼資料中的標示</th><th scope="col">Claude 表單中的選項</th><th scope="col">我們的伺服器（2026年10月3日）</th></tr></thead>
<tbody>
<tr><th scope="row">預先註冊</th><td>預先建立的用戶端 ID，手動輸入或內建在用戶端中</td><td>不需要</td><td>Use your own OAuth client（使用自己的 OAuth 用戶端）</td><td>未提供</td></tr>
<tr><th scope="row">用戶端 ID 中繼資料文件</th><td><code>client_id</code> 本身是一個 HTTPS URL，指向用戶端代管的 JSON 檔案，由伺服器去讀取</td><td><code>client_id_metadata_document_supported</code></td><td>Use Claude’s published identity（推薦，使用 Claude 已發布的身分）</td><td>未宣告支援</td></tr>
<tr><th scope="row">動態用戶端註冊</th><td>把自己的中繼資料 POST 到註冊端點，取得 <code>client_id</code></td><td><code>registration_endpoint</code></td><td>Register automatically（自動註冊）</td><td>已宣告支援</td></tr>
</tbody>
</table>
</div>
<p>我們在 2026年10月3日再次讀取 Supabase 的中繼資料：其中列出了註冊端點、S256 和權杖端點驗證方式 <code>none</code>，沒有 CIMD 標示；它的 MCP 指南會請你在儀表板中開啟動態註冊。</p>
<p>這決定了使用者在 Claude 自訂連接器表單中該怎麼選。Anthropic 的文件把推薦選項，也就是 Claude 已發布的身分（published identity），描述為由 Anthropic 代管、需要伺服器支援的 CIMD。所以我們的指南要求選擇「Sign in now」（我們沒有任何工具能在不登入的情況下使用）和「Register automatically」。Anthropic 的開發者文件說，伺服器未宣告支援 CIMD 時，Claude 會退回使用 DCR（CIMD 還要求 <code>token_endpoint_auth_methods_supported</code> 中包含 <code>none</code>）；我們沒測試過保留表單預設值的情況，所以要求明確選擇。我們在 2026年10月2日讀過的用戶端文件顯示，ChatGPT 開發者模式、Cursor、VS Code 和 Gemini CLI 都使用 DCR。</p>
<p>DCR 也有代價：Anthropic 提到，Claude 每次全新連線都會註冊一個新的用戶端；Supabase 也提醒，開啟動態註冊後，任何 MCP 用戶端都能在你的專案中註冊。不過，註冊本身不會授予任何權限。使用者仍然要登入並核准這個用戶端，權杖驗證也會在每個請求上檢查帳號。由於任何用戶端都能用任意名稱註冊，從 2026年10月6日起，我們的授權同意頁面會顯示核准後使用者將被帶往何處；如果某個應用程式自稱 ChatGPT 或 Claude，重新導向網址卻不是該助理的網址，頁面就會發出警告。</p>

<h2 id="mcp-refresh-tokens">存取權杖到期後，MCP 用戶端還能維持登入嗎？</h2>
<p>可以。存取權杖的效期是一小時，用戶端會用更新權杖續期。在依賴這一點之前，我們讀過 Supabase Auth 的原始碼（<code>master</code> 分支，截至 2026年9月22日）：</p>
<ul>
<li>每次授權碼交換都會回傳更新權杖，無論是否申請了 <code>offline_access</code>；</li>
<li>更新必須由該工作階段所屬的用戶端發起，更新時會輪替更新權杖，並保留 <code>client_id</code> claim；</li>
<li>更新時 hook 同樣會執行（此時 <code>authentication_method</code> 為 <code>token_refresh</code>），所以每次都會重新寫入我們的受眾和 scope。</li>
</ul>
<p>2026年10月2日在正式環境的實測結果一致：更新權杖有輪替，續期後的存取權杖也能正常使用。Anthropic 的文件說，Claude 會在收到 401 後，以及權杖到期前五分鐘內更新權杖；如果授權伺服器在 <code>scopes_supported</code> 中列出 <code>offline_access</code>，Claude 也會申請它。Supabase 的中繼資料有列出它，而我們額外申請 <code>offline_access</code> 的檢查也沒有問題。</p>
<p>以下情況仍會結束工作階段：</p>
<ul>
<li>帳號被停用，或使用者被刪除、停權（在我們的正式環境實測中，停用帳號後，瀏覽器工作階段和兩個 MCP 權杖都被拒絕）；</li>
<li>舊的更新權杖在 Supabase 的重複使用間隔（預設 10 秒）之後又被使用，這會讓整個工作階段失效；</li>
<li>開啟了工作階段時限、閒置逾時或「single session per user」（每位使用者只保留一個工作階段）。最後這項務必關閉，否則在網站上登入一次，助理的工作階段就會結束；</li>
<li>使用者以 Supabase 預設的 <code>logout</code> 登出網站，這會結束該帳號的所有工作階段，包括助理的。我們的網站在 2026年10月6日之前就是如此，這很可能就是某個 ChatGPT 連線無法再續期權杖的原因；現在「登出」會送出 <code>scope=local</code>，只結束目前這個瀏覽器的工作階段。</li>
</ul>
<p>Supabase 沒有公布 revocation 端點（2026年10月3日確認），所以我們切斷存取權的方式就是停用帳號。</p>

<h2 id="oauth-issuer-trailing-slash">issuer 結尾多一個斜線，為什麼會出問題？</h2>
<p>因為 issuer 是逐字元比對的。RFC 8414 要求中繼資料中的 <code>issuer</code> 與用戶端用來找到它的值完全相同，RFC 9728 對 <code>resource</code> 也有同樣要求。對嚴格的用戶端來說，<code>https://auth.example.com</code> 和 <code>https://auth.example.com/</code> 是兩個不同的 issuer。</p>
<p>MCP Python SDK 以 pydantic 的 <code>AnyHttpUrl</code> 儲存 issuer，而我們 lockfile 中鎖定的 pydantic 2.13.5 會把 <code>https://auth.example.com</code> 變成 <code>https://auth.example.com/</code>。由此產生的中繼資料，寫的是授權伺服器從未公布過的 issuer，所以我們的伺服器自己產生受保護資源中繼資料，issuer 與設定值完全一致。Supabase 的 issuer 以路徑結尾，pydantic 不會改動，所以我們在正式環境中從沒遇過這個問題；但如果 issuer 只是不帶路徑的來源（origin），就會中招。現在只要授權伺服器的 <code>issuer</code> 與我們的不完全相同，部署檢查就會失敗。</p>

<h2 id="verified-and-not-verified">哪些已經驗證，哪些還沒有</h2>
<p>最初的證據來自一個依 MCP 規範撰寫的腳本用戶端。2026年10月2日，我們對正式環境跑了一輪測試，涵蓋沒帶權杖時的 401、公用用戶端的動態註冊、帶 <code>resource</code> 的授權同意與授權碼交換、帶輪替的權杖更新、用兩個權杖分別呼叫 <code>tools/list</code>，以及停用帳號（停用後兩個權杖和瀏覽器工作階段都被拒絕）。第二輪額外申請了 <code>offline_access</code>，兩輪結果都沒有問題。那次測試用的還是我們先前的網址；搬到 <code>mcp.scorestarling.com</code> 的過程<a href="https://scorestarling.com/zh-hant/blog/mcp-server-url">另有一篇文章</a>。</p>
<p>此後，真實的用戶端也連上了。2026年10月3日，ChatGPT 開發者模式中的應用程式連上 <code>mcp.scorestarling.com</code>，完成登入並呼叫了一個工具；10月6日，Claude 也成功連線並執行了工具。尚未驗證的有：</p>
<ul>
<li>權杖真正到期後的更新：腳本中的更新是在登入後立刻執行的，我們還沒追蹤過真實用戶端的更新；</li>
<li>Supabase 代管端的工作階段設定，我們還沒有讀回確認。</li>
</ul>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization">MCP 規範（2025-11-25 版）：授權</a> — Model Context Protocol</li>
<li><a href="https://www.rfc-editor.org/rfc/rfc9728">RFC 9728：OAuth 2.0 受保護資源中繼資料</a> — IETF</li>
<li><a href="https://www.rfc-editor.org/rfc/rfc6750#section-3.1">RFC 6750：OAuth 2.0 授權架構：Bearer 權杖的使用，§3.1</a> — IETF</li>
<li><a href="https://www.rfc-editor.org/rfc/rfc8707">RFC 8707：OAuth 2.0 資源指標</a> — IETF</li>
<li><a href="https://www.rfc-editor.org/rfc/rfc7591">RFC 7591：OAuth 2.0 動態用戶端註冊協定</a> — IETF</li>
<li><a href="https://www.rfc-editor.org/rfc/rfc8414">RFC 8414：OAuth 2.0 授權伺服器中繼資料</a> — IETF</li>
<li><a href="https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/">OAuth 用戶端 ID 中繼資料文件（網際網路草案）</a> — IETF OAuth 工作小組</li>
<li><a href="https://claude.com/docs/connectors/building/authentication">連接器的身分驗證</a> — Anthropic</li>
<li><a href="https://claude.com/docs/connectors/custom/remote-mcp">新增不在目錄中的連接器</a> — Anthropic</li>
<li><a href="https://supabase.com/docs/guides/auth/oauth-server/oauth-flows">OAuth 2.1 流程</a> — Supabase</li>
<li><a href="https://supabase.com/docs/guides/auth/auth-hooks/custom-access-token-hook">自訂存取權杖 Hook</a> — Supabase</li>
<li><a href="https://supabase.com/docs/guides/auth/oauth-server/mcp-authentication">Model Context Protocol（MCP）身分驗證</a> — Supabase</li>
<li><a href="https://supabase.com/docs/guides/auth/sessions">使用者工作階段</a> — Supabase</li>
<li><a href="https://github.com/supabase/auth/issues/2820">supabase/auth 的 issue #2820</a> — GitHub</li>
</ol>
</section><h2>常見問題</h2><h3>遠端 MCP 伺服器需要自己的授權伺服器嗎？</h3><p>不需要。MCP 授權規範允許授權伺服器是獨立的服務。MCP 伺服器發布 RFC 9728 中繼資料指出授權伺服器，再驗證它簽發的權杖，包括權杖的受眾。我們的伺服器指向 Supabase Auth，自己只負責驗證權杖。</p><h3>請求沒有帶權杖時，MCP 伺服器的 401 該包含 invalid_token 錯誤嗎？</h3><p>不該。RFC 6750 §3.1 指出，不帶認證資訊的請求不應收到錯誤碼。應改為提供 <code>resource_metadata</code> 和 <code>scope</code> 提示，把 <code>invalid_token</code> 留給帶了權杖卻遭拒絕的情況，用來告訴用戶端更新權杖。</p><h3>Supabase Auth 能為 MCP 伺服器簽發自訂 scope 嗎？</h3><p>截至 2026年10月3日還不能：Supabase 的 OAuth 2.1 文件寫明不支援自訂 scope。我們請求的是 <code>openid email profile</code>，再透過自訂存取權杖 hook（custom access token hook）加上我們自己的 scope 和 MCP 受眾；每次更新權杖時，這個 hook 也會執行。</p><h3>伺服器不支援 CIMD 時，在 Claude 的自訂連接器表單中該怎麼選？</h3><p>在「OAuth client」下選「Register automatically」，也就是動態用戶端註冊；如果每個工具都需要帳號，再在「Authentication」下選「Sign in now」。Claude 推薦的選項「Use Claude’s published identity」是一份用戶端 ID 中繼資料文件，需要伺服器支援。我們的 <a href="https://scorestarling.com/zh-hant/mcp#claude">Claude 連線步驟</a>對這兩項選擇都有說明。</p><h3>要從 Supabase 取得更新權杖，一定要申請 offline_access scope 嗎？</h3><p>在我們的測試中不需要。不申請它，Supabase 在每次授權碼交換時也都回傳了更新權杖：從原始碼看是如此，2026年10月2日在正式環境的實測也是如此。有些用戶端還是會申請它；Anthropic 的文件說，授權伺服器有列出它時 Claude 就會申請，而我們帶上它的檢查也沒有問題。</p>]]></content:encoded>
    </item>
    <item>
      <title>MCP 伺服器該放在哪個網址？我們查了 33 個</title>
      <link>https://scorestarling.com/zh-hant/blog/mcp-server-url</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/mcp-server-url</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>技術</category>
      <description>2026年10月2日，我們探測了 33 個公開 MCP 端點，要求登入的 22 個服務中有 14 個使用 mcp.&lt;domain&gt;/mcp。另附我們選定網址並完成搬遷的過程。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>我們查過的遠端 MCP 伺服器，大多有自己獨立的網域。2026年10月2日，在端點要求登入的 22 個服務中，14 個使用 <code>mcp.&lt;domain&gt;/mcp</code>，另有 6 個使用 <code>mcp.</code> 網域但不帶路徑或帶版本路徑，還有 2 個使用 <code>api.</code> 網域。網址沒有統一標準，路徑也猜不到，所以要公布確切的 URL。我們把自己的網址從 <code>scorestarling.com/mcp</code> 搬到 <code>https://mcp.scorestarling.com/mcp</code>，好讓網站上的 <code>/mcp</code> 成為給人看的連線指南。</p>
</section>

<h2 id="mcp-server-url-survey">公開的 MCP 伺服器都用什麼 URL？</h2>
<p>大多是專用的 <code>mcp.</code> 子網域，路徑通常是 <code>/mcp</code>。當時我們正在 <code>mcp.scorestarling.com/mcp</code> 和 ElevenLabs 那種寫法 <code>api.scorestarling.com/v1/mcp</code> 之間猶豫，想知道其他服務實際上怎麼做，就去查了一輪。</p>
<p>2026年10月2日 17:40 至 17:42（UTC），我們對 27 個服務的 33 個網址各送出一個不帶驗證資訊的 POST 請求：請求內容為 <code>{}</code>，帶有 <code>Accept: application/json, text/event-stream</code> 標頭，逾時 8 秒，只記錄狀態碼。401 代表這個網址上有服務回應且需要權杖，受保護的 MCP 伺服器正是這樣開始登入流程的。404 代表這個路徑下沒有任何服務。有些網址是各服務公開的，有些則是我們為了看哪些路徑會回應而嘗試的變體和猜測。這只是一些知名服務的快照，不是普查，其中任何一個之後都可能有所變動。</p>
<div class="table-scroll" role="region" aria-label="要求登入的 MCP 伺服器網址模式" tabindex="0">
<table>
<caption>回傳 401 的 22 個服務的網址模式（2026年10月2日）</caption>
<thead><tr><th scope="col">模式</th><th scope="col" class="num">服務數</th><th scope="col">回傳 401 的網址</th></tr></thead>
<tbody>
<tr><th scope="row">mcp. 子網域，路徑為 /mcp</th><td class="num">14</td><td><code>mcp.notion.com/mcp</code>, <code>mcp.linear.app/mcp</code>, <code>mcp.sentry.dev/mcp</code>, <code>mcp.supabase.com/mcp</code>, <code>mcp.canva.com/mcp</code>, <code>mcp.higgsfield.ai/mcp</code>, <code>mcp.runwayml.com/mcp</code>, <code>mcp.airtable.com/mcp</code>, <code>mcp.gamma.app/mcp</code>, <code>mcp.posthog.com/mcp</code>, <code>mcp.wix.com/mcp</code>, <code>mcp.intercom.com/mcp</code>, <code>mcp.monday.com/mcp</code>, <code>mcp.paypal.com/mcp</code></td></tr>
<tr><th scope="row">mcp. 子網域，不帶路徑或帶版本路徑</th><td class="num">6</td><td><code>mcp.stripe.com</code>、<code>mcp.vercel.com</code>、<code>mcp.box.com</code>、<code>mcp.miro.com</code>；<code>mcp.atlassian.com/v1/mcp</code>、<code>mcp.asana.com/v2/mcp</code>（Asana 的 <code>mcp.asana.com/sse</code> 也回傳了 401）</td></tr>
<tr><th scope="row">api. 子網域，帶路徑</th><td class="num">2</td><td><code>api.elevenlabs.io/v1/mcp</code>、<code>api.githubcopilot.com/mcp/</code>（GitHub）</td></tr>
<tr><th scope="row">主網站或文件網站上的路徑</th><td class="num">0</td><td>沒有；<code>huggingface.co/mcp</code> 和 <code>learn.microsoft.com/api/mcp</code> 回傳了 400</td></tr>
<tr><th scope="row">合計</th><td class="num">22</td><td>23 個網址，因為 Asana 在兩個網址上都回傳了 401</td></tr>
</tbody>
</table>
</div>
<div class="table-scroll" role="region" aria-label="沒有回傳 401 的 MCP 網址" tabindex="0">
<table>
<caption>沒有回傳 401 的 10 個網址</caption>
<thead><tr><th scope="col">結果</th><th scope="col">網址</th><th scope="col">代表什麼</th></tr></thead>
<tbody>
<tr><th scope="row">404</th><td><code>mcp.stripe.com/mcp</code>, <code>mcp.vercel.com/mcp</code>, <code>mcp.higgsfield.ai</code>, <code>api.elevenlabs.io/mcp</code></td><td>該路徑下沒有端點；這幾個服務都在同一網域的另一個路徑上回傳了 401</td></tr>
<tr><th scope="row">400</th><td><code>mcp.figma.com/mcp</code>, <code>huggingface.co/mcp</code>, <code>learn.microsoft.com/api/mcp</code>, <code>docs.mcp.cloudflare.com/mcp</code></td><td>有服務回應，但拒絕了我們的空白請求；我們沒有深究</td></tr>
<tr><th scope="row">沒有回應</th><td><code>mcp.suno.com/mcp</code>, <code>mcp.elevenlabs.io/mcp</code></td><td>8 秒內沒有任何 HTTP 回應</td></tr>
</tbody>
</table>
</div>
<p>最實用的教訓來自那幾個 404：MCP 的 URL 無法從網域推斷出來。Stripe 和 Vercel 在不帶路徑的網域上回應，存取 <code>/mcp</code> 反而回傳 404；Higgsfield 正好相反；ElevenLabs 則一定要帶上 <code>/v1</code>。用戶端只會原封不動地使用拿到的 URL，而 RFC 9728 把這個 URL 定為伺服器中繼資料必須相符的識別，所以文件、安裝連結和外掛程式資訊清單裡必須是同一個字串。外掛程式資訊清單的寫法更是五花八門：在我們安裝過的外掛程式中，有直接放在 gitlab.com 主網站路徑上的 <code>gitlab.com/api/v4/mcp</code>，也有 <code>mcp.hubspot.com/anthropic</code>。這兩個我們沒有探測。</p>

<h2 id="mcp-endpoint-separate-host">MCP 端點該和網站共用同一個網域嗎？</h2>
<p>我們認為不該。端點是給軟體用的，連線指南是給人看的，兩者都想佔用那個最直覺的網址。2026年10月2日之前，<code>scorestarling.com/mcp</code> 是我們的 MCP 伺服器，指南只能放在別處；而且每個用戶端都是靠這個 URL 回傳的 401 找到登入入口，它不可能同時又是一個網頁。</p>
<p>我們考慮過在同一個 URL 上同時提供兩者，對瀏覽器回傳網頁、對用戶端回傳 MCP，但沒有把它當成長期做法：對請求的判斷只要錯一次，所有用戶端都會找不到登入入口。傳輸規範還允許用戶端對端點送出 GET 請求來開啟事件串流，預期的回覆是事件串流或 405，而不是 HTML 網頁。</p>
<p>獨立的網域還能讓網站的改動影響不到端點：網站改版動不了它，適合行銷網站的 CDN 快取或瀏覽器人機驗證也永遠不會擋在它前面。AI 助理是從自己的基礎架構、而不是從瀏覽器呼叫伺服器：Anthropic 的連接器文件說，Claude 會從 Anthropic 的雲端發起連線，而身分識別提供者前面的防火牆可能導致登入失敗。我們看過的兩家同業也是這樣拆分（2026年10月2日確認，10月3日再次確認）：Higgsfield 的指南在 <code>higgsfield.ai/mcp</code>，伺服器在 <code>mcp.higgsfield.ai/mcp</code>；ElevenLabs 的指南在 <code>elevenlabs.io/mcp</code>，伺服器在 <code>api.elevenlabs.io/v1/mcp</code>。</p>
<p>我們的 MCP 網域只提供 <code>/mcp</code> 和 <code>/.well-known/*</code>。其他路徑一律以 308 重新導向到網站上相同的路徑和查詢參數，讓登入、授權同意、網頁、下載連結和 Cookie 都留在同一個網域。網站上的 <a href="https://scorestarling.com/zh-hant/mcp">/mcp</a> 現在是 Claude、ChatGPT 等用戶端的連線指南。</p>
<pre><code>$ curl -si "https://mcp.scorestarling.com/login?x=1"
HTTP/2 308
location: https://scorestarling.com/login?x=1</code></pre>

<h2 id="mcp-url-pattern">該選 mcp.example.com/mcp、不帶路徑的網域，還是 api.example.com/v1/mcp？</h2>
<p>三種都可以，看這個網域上還放了什麼。MCP 授權規範把 <code>https://mcp.example.com/mcp</code> 和 <code>https://mcp.example.com</code> 都列為有效的標準伺服器 URI，並要求採用不帶結尾斜線的形式，除非斜線有實際意義。傳輸規範只要求一個同時接受 POST 和 GET 的端點路徑，範例是 <code>https://example.com/mcp</code>；Anthropic 的連接器文件則用 <code>https://mcp.example.com/mcp</code>。</p>
<div class="table-scroll" role="region" aria-label="我們評估過的網址方案" tabindex="0">
<table>
<caption>我們為 ScoreStarling 評估過的方案</caption>
<thead><tr><th scope="col">方案</th><th scope="col">採用者（2026年10月2日）</th><th scope="col">對我們而言</th></tr></thead>
<tbody>
<tr><th scope="row">mcp. 網域 + /mcp（最終選擇）</th><td>14 個服務，包括 Notion、Linear、Canva、Higgsfield 和 Runway</td><td>樣本中最常見的模式；我們的伺服器原本就在 <code>/mcp</code> 上回應，只需要換網域</td></tr>
<tr><th scope="row">不帶路徑的 mcp. 網域</th><td>Stripe、Vercel、Box、Miro</td><td>最短，但要改更多程式碼，還得先多發布一個版本</td></tr>
<tr><th scope="row">api. 網域 + /v1/mcp</th><td>ElevenLabs；GitHub 用的是 <code>api.githubcopilot.com/mcp/</code></td><td>適合在這個網域上提供公開開發者 API 的公司；我們沒有</td></tr>
</tbody>
</table>
</div>
<p>還有兩條小規則。Streamable HTTP 端點不要以 <code>/sse</code> 結尾：Anthropic 的文件說，在 Claude 的連接器表單裡，以它結尾的 URL 會選用舊的 SSE 傳輸方式。另外，除非真有需要，不要加結尾斜線；回傳 401 的 23 個網址裡，只有 GitHub 的帶有斜線。</p>

<h2 id="move-mcp-server-url">如何把 MCP 伺服器搬到新的 URL？</h2>
<p>讓新舊兩個網址並行一段時間，而且每個網址都要能獨立完整運作：有自己的 401、自己的中繼資料，兩種受眾的權杖都能用。我們分四步完成搬遷，在 2026年10月2日晚間（UTC）一小時內全部上線：</p>
<ol>
<li><p>趁還沒有任何地方用到，先上線別名支援。用一項設定列出以前的網址，每個都必須是 HTTPS、以 <code>/mcp</code> 結尾。權杖的受眾是目前網址或某個別名時才算有效，其他受眾一律拒絕。主機允許清單（Starlette 的信任主機清單和 MCP SDK 的 DNS 重新繫結防護）包含所有網址以及網站本身。沒有設定別名時，正式環境的行為和以前完全一樣。</p></li>
<li><p>新增網域，然後在同一次變更中切換網址並設定別名。新子網域先設好 DNS 記錄，大約 6 分鐘後憑證也簽發了；在我們的伺服器被設定為信任它之前，這個網域一直回傳 400。一次設定變更既設好新的資源 URL，也把舊網址列為別名，所以只要重新部署一次，兩者就同時生效。如果在第 1 步上線前就這麼做，執行中的程式會拒絕網站自己的網域。</p></li>
<li><p>把所有會被人複製的地方都改指向新網址：連線指南（包括為 Claude、Cursor 和 VS Code 編碼好的一鍵安裝連結）、外掛程式資訊清單和文件。</p></li>
<li><p>切換權杖受眾，再停用舊網址。一次資料庫遷移讓我們的存取權杖 hook 改為簽發新的受眾，CI 在程式碼上線前就套用了這次遷移；別名還在時，兩種受眾的權杖都照常可用。接著我們刪除別名並重新部署。在那次發布和重新部署之間的幾分鐘裡，網站的 <code>/mcp</code> 對瀏覽器回傳指南、對 MCP 用戶端回傳伺服器。現在它只提供指南，而指南原本的網址 <code>/connect</code> 會以 308 重新導向過來。</p></li>
</ol>
<p>最容易漏掉的是中繼資料。RFC 9728 要求其中的 <code>resource</code> 與用戶端連線時使用的 URL 完全相同，Anthropic 的文件也要求它與使用者在 Claude 中輸入的 URL 一致。所以在新舊並行期間，每個網域都描述自己，每個 401 也都指向本網域的中繼資料文件：</p>
<pre><code>GET https://scorestarling.com/.well-known/oauth-protected-resource/mcp
→ "resource": "https://scorestarling.com/mcp"

GET https://mcp.scorestarling.com/.well-known/oauth-protected-resource/mcp
→ "resource": "https://mcp.scorestarling.com/mcp"</code></pre>
<p>我們沒有對 <code>/mcp</code> 本身做重新導向。傳輸規範沒有說明端點的重新導向，而端點 URL 作為資源和權杖受眾，是 OAuth 交握的一部分，所以我們選擇在兩個網址上同時提供 MCP。其他路徑則用 308 而不是 301，這樣能保留請求方法，因為 RFC 9110 不允許用戶端在收到 308 後把 POST 改成 GET。受眾是怎麼寫進權杖的，請見<a href="https://scorestarling.com/zh-hant/blog/remote-mcp-server-oauth">我們關於 MCP OAuth 的筆記</a>。</p>
<p>我們能搬得這麼快，是因為 ScoreStarling 當時還是僅限邀請的小規模試營運，還沒有人依賴舊網址。別名刪除後，仍設定著 <code>scorestarling.com/mcp</code> 的用戶端必須重新連線。依我們維運手冊的預期，已改用新網址、手上卻還是舊受眾權杖的用戶端，會收到一次 401 <code>invalid_token</code>，接著更新權杖、取得新受眾；不過我們還沒親眼看過真實的 Claude 或 ChatGPT 用戶端這樣做。如果已經有真實使用者，就保留別名，直到舊網址不再有流量。</p>

<h2 id="what-broke">搬遷過程中出了哪些問題？</h2>
<p>過程中出了三個小問題：</p>
<ul>
<li>有個檢查腳本用 <code>resource.replace('/mcp', '/.well-known/oauth-protected-resource/mcp')</code> 組出中繼資料 URL。在舊網址上沒問題；換成 <code>https://mcp.scorestarling.com/mcp</code> 後，<code>//mcp.</code> 裡的 <code>/mcp</code> 也被比對到了，結果變成 <code>https://.well-known/oauth-protected-resource/mcp.scorestarling.com/.well-known/oauth-protected-resource/mcp</code>。正確做法是先剖析 URL，再依各部分組合：通訊協定和主機名稱，接著是 well-known 區段，最後是路徑。</li>
<li>網址變長後，在 320 像素寬的螢幕上撐破了指南頁面的版面，直到我們允許行內程式碼換行才解決。</li>
<li>用 Railway 的命令列工具刪除別名變數，只會把變更暫存起來；直到我們手動重新部署，舊網址都還是會被接受。</li>
</ul>

<h2 id="mcp-url-checklist">MCP 伺服器網址選擇清單</h2>
<ul>
<li>給端點一個專用網域，不在上面放任何給人看的內容；在我們的樣本中，常見做法是 <code>mcp.</code> 加上你的網域。</li>
<li>路徑一次定好。<code>/mcp</code> 最常見，不帶路徑的網域也有效；不要加結尾斜線，也不要讓路徑以 <code>/sse</code> 結尾。</li>
<li>給人看的連線指南放在主網站上，用一個猜得到的網址，並在導覽列放上連結。</li>
<li>為這個確切的 URL 提供 RFC 9728 中繼資料：放在 <code>/.well-known/oauth-protected-resource</code> 加上你的路徑之處，根路徑下也放一份，其中的 <code>resource</code> 要等於用戶端使用的網址。</li>
<li>把這個 URL 設為權杖受眾，並在需要搬遷之前就做好別名支援。</li>
<li>MCP 網域上的其他所有路徑都以 308 重新導向到你的網站，並保留查詢字串。</li>
<li>MCP 網域上不要啟用瀏覽器人機驗證和 HTML 快取。</li>
<li>所有地方（指南、安裝連結、外掛程式資訊清單、文件）都只公布同一個字串，並測試每一處是否一致。</li>
<li>搬遷順序：先做別名支援，再於同一次變更中切換新網址、設定別名，接著修改每一處引用、切換受眾，等舊網址沒有流量後再停用。</li>
</ul>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/transports">MCP 規範（2025-11-25 版）：傳輸</a> — Model Context Protocol</li>
<li><a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization">MCP 規範（2025-11-25 版）：授權</a> — Model Context Protocol</li>
<li><a href="https://www.rfc-editor.org/rfc/rfc9728">RFC 9728：OAuth 2.0 受保護資源中繼資料</a> — IETF</li>
<li><a href="https://www.rfc-editor.org/rfc/rfc9110#section-15.4.9">RFC 9110：HTTP 語意，§15.4.9 308 永久重新導向</a> — IETF</li>
<li><a href="https://claude.com/docs/connectors/custom/remote-mcp">新增不在目錄中的連接器</a> — Anthropic</li>
<li><a href="https://claude.com/docs/connectors/building/authentication">連接器的身分驗證</a> — Anthropic</li>
<li><a href="https://higgsfield.ai/mcp">Higgsfield MCP</a> — Higgsfield</li>
<li><a href="https://elevenlabs.io/mcp">ElevenLabs Agents &amp; Creative MCP</a> — ElevenLabs</li>
</ol>
</section><h2>常見問題</h2><h3>MCP 伺服器的 URL 一定要以 /mcp 結尾嗎？</h3><p>不用。MCP 規範只要求單一端點路徑，並把 <code>https://mcp.example.com</code> 和 <code>https://mcp.example.com/mcp</code> 都列為有效的伺服器 URI。在我們 2026年10月2日的樣本中，要求登入的 22 個服務裡有 18 個使用 <code>/mcp</code> 或 <code>/v1/mcp</code> 這類路徑，另外 4 個直接在不帶路徑的網域上回應。選定一種，公布時一字不差即可。</p><h3>連線指南頁面和 MCP 端點可以共用一個 URL 嗎？</h3><p>可以在同一個 URL 上對瀏覽器回傳網頁、對 MCP 用戶端回傳伺服器，但除了搬遷期間的幾分鐘，我們沒有這樣做。只要誤判一次請求，用戶端就找不到登入入口；而且 Streamable HTTP 用戶端可能會對端點送出 GET 請求，預期收到事件串流或 405。我們的指南在 scorestarling.com/mcp，伺服器則有自己的網域。</p><h3>更換 MCP 伺服器的 URL，會讓使用者斷線嗎？</h3><p>只有停用舊網址時才會。這個 URL 既是 OAuth 的資源（resource），也是權杖的受眾（audience），所以舊網址一旦不再回應，仍設定著舊網址的用戶端就得重新連線。過渡期間讓兩個網址同時提供服務：兩種受眾都接受、每個網址各有自己的中繼資料，等舊網址沒有流量了再停用。</p><h3>對 MCP URL 送出 POST 請求，回傳 401、404 或 400 各代表什麼？</h3><p>在我們的調查中，401 代表受保護的端點有回應並要求權杖，404 代表這個路徑下什麼都沒有。400 只能說明某個環節拒絕了我們空白的測試請求內容，我們沒有進一步解讀。要正確測試伺服器，請用真正的 MCP 用戶端連線。</p><h3>MCP 伺服器的 URL 結尾要加斜線嗎？</h3><p>最好不要。MCP 授權規範要求實作採用不帶結尾斜線的形式，除非斜線有實際意義。在我們調查中回傳 401 的 23 個網址裡，只有 GitHub 的 <code>api.githubcopilot.com/mcp/</code> 帶有斜線。</p>]]></content:encoded>
    </item>
    <item>
      <title>莫札特的椋鳥：我們名字的由來</title>
      <link>https://scorestarling.com/zh-hant/blog/mozart-starling</link>
      <guid isPermaLink="true">https://scorestarling.com/zh-hant/blog/mozart-starling</guid>
      <pubDate>Sat, 03 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Haiyi Mei</dc:creator>
      <category>故事</category>
      <description>1784年5月27日，莫札特花 34 克羅伊策買下一隻椋鳥，它會唱莫札特剛寫好的 K. 453 終樂章主題，還自己加了一個停頓、唱出一個升 G。我們查證了原始紀錄。</description>
      <content:encoded><![CDATA[<section class="answer" aria-labelledby="short-answer">
<h2 id="short-answer">先說結論</h2>
<p>1784年5月27日，莫札特花 34 克羅伊策買下一隻椋鳥。這隻鳥會唱他 G 大調第十七號鋼琴協奏曲（K. 453）終樂章的開頭，而這首協奏曲大約六週前才登記進他的作品目錄。他在帳本上寫下了鳥兒唱的版本——在一個音上停頓，把協奏曲裡的 G 唱成升 G——還在下面加了一句「Das war schön!」（「真美啊！」）。這隻鳥在他身邊生活了三年；1787年6月4日它死後，莫札特把它葬在花園裡，還為它寫了一首打油詩。</p>
</section>

<h2 id="the-1784-purchase">1784 年買鳥：帳本裡的那條紀錄</h2>
<p>1784年5月27日，莫札特在維也納買下了這隻椋鳥；關於它最早的紀錄，就是他當天記在帳本（也叫支出簿）上的一條。這本帳是他那年開始記的，和作品目錄同時起筆。維基百科援引 Hermann Abert 的莫札特傳記稱，這本支出簿他只記了大約一年，而作品目錄一直記到他 1791 年去世。</p>
<p>《新莫札特全集》（Neue Mozart-Ausgabe，簡稱 NMA）是莫札特全部作品的學術校訂版，由國際莫札特基金會在網路上發布。收錄 K. 453 的那一卷在前言裡影印了這條紀錄。紀錄很短：最上面一行是日期、「Vogel Stahrl」（椋鳥）和「34 Kr.」；下面是一行五線譜，G 大調，2/2 拍；譜表下方，莫札特寫著「Das war schön!」</p>
<p>「Kr.」是克羅伊策（kreuzer）的縮寫，一種當時的硬幣，60 克羅伊策合 1 古爾登。NMA 的英譯本把它印成「34 Crowns」（34 克朗），那句話則譯作「That was nice!」（真不錯！）；維基百科則依據 Otto Erich Deutsch 編的莫札特文獻傳記引用德文原文，讀作克羅伊策，譯作「That was beautiful!」（真美啊！），我們的首頁用的也是這個譯法。該卷 NMA 的編者 Eva 和 Paul Badura-Skoda 說，莫札特顯然很喜歡聽這隻鳥用哨音唱出協奏曲最後一個樂章的主題，於是買下了它，「哪怕音準可能並不十分乾淨」。</p>

<h2 id="the-tune-bar-by-bar">曲調對照：K. 453 終樂章主題，逐小節看</h2>
<p>這隻鳥唱的是協奏曲終樂章開頭那個主題的第一個樂句：一個弱起加四個小節，但有兩處改動——在第一個完整小節末尾的 G 上停了一下，又在下一小節把 G 唱成了升 G。莫札特的紀錄把這兩處都保留了下來。</p>
<p>這首協奏曲是莫札特為學生芭芭拉（芭貝特）·普洛耶爾寫的，1784年4月12日登記進作品目錄。終樂章是 2/2 拍的 Allegretto（小快板），由一個樸素、好唱的主題和一組變奏構成，最後以 Presto（急板）收尾。下表把這個主題的開頭和帳本上那行譜並排列出。</p>
<div class="table-scroll" role="region" aria-label="K. 453 終樂章主題與椋鳥版本對照" tabindex="0">
<table>
<caption>K. 453 終樂章主題開頭樂句與椋鳥版本逐小節對照（僅列音高）</caption>
<thead><tr><th scope="col">小節</th><th scope="col">協奏曲（K. 453 終樂章）</th><th scope="col">椋鳥（莫札特的紀錄）</th><th scope="col">差別</th></tr></thead>
<tbody>
<tr><th scope="row">弱起</th><td>B C（兩個八分音符）</td><td>B C</td><td>無</td></tr>
<tr><th scope="row">1</th><td>D D D G</td><td>D D D G，G 上有延長記號（停頓）</td><td>椋鳥把 G 拖長了</td></tr>
<tr><th scope="row">2</th><td>F♯ F♯ G G</td><td>F♯ F♯ G♯ G♯</td><td>G 唱成了升 G</td></tr>
<tr><th scope="row">3</th><td>A A，接八分音符 A B C A</td><td>相同</td><td>無</td></tr>
<tr><th scope="row">4</th><td>B（二分音符），主題繼續</td><td>B（二分音符）；紀錄到此結束</td><td>無</td></tr>
</tbody>
</table>
</div>
<p class="note">音高依據維基百科對這條紀錄的轉寫（引自 Deutsch），以及它依照音樂學家 Daniel Heartz 校正過的曲調。我們把兩者都和 NMA 核對過：NMA 影印的紀錄裡有延長記號和升記號，而它收錄的終樂章樂譜（該卷第 46 頁）裡兩者都沒有。協奏曲裡還有裝飾音、跳音記號和一條圓滑線，帳本上那行譜都沒有寫。</p>
<p>如果把它當作一份扒譜來讀，這條紀錄相當用心。莫札特對自己的曲子再清楚不過，卻仍把聽到的停頓和升記號照實寫下，而不是順手改掉。這行譜記錄的是那一次演唱，而不是作品本身。</p>

<h2 id="how-the-bird-knew-the-tune">這隻鳥怎麼會唱一首才寫了六週的曲子？</h2>
<p>沒人知道。文獻只記下了買鳥這件事，沒記它是怎麼學會這段曲調的；幾種主要說法講的先後順序也不一樣：</p>
<ul>
<li>NMA 的前言說，莫札特先聽到這隻鳥用哨音唱出這個主題，才因此買下了它。</li>
<li>維基百科的「歐洲椋鳥」條目援引《<cite>Birds Britannica</cite>》（2005）一書，說莫札特是在一家店裡聽到它唱出自己六週前寫成、還未公開演出過的作品中的一個樂句，才買下了它。</li>
<li>維基百科的「莫札特的椋鳥」條目則認為順序正好相反：大概是莫札特在店裡（或別的什麼買鳥的地方）把曲子教給了這隻鳥。</li>
</ul>
<p>「尚未公開演出」這一點也不確定。NMA 前言引用了莫札特 1784年6月12日寫給父親的一封信，信中說，芭貝特·普洛耶爾第二天會在普洛耶爾家位於德布林（Döbling）的宅邸舉辦的音樂晚會上，演奏她的新 G 大調協奏曲。據維基百科協奏曲條目的概述，音樂學家 Michael Lorenz 認為莫札特可能更早就演奏過這首曲子：1784年4月29日，他與小提琴家 Regina Strinasacchi 在克恩滕門劇院（Kärntnertortheater）舉辦過一場音樂會。如果真是這樣，那麼在莫札特買鳥的四週前，這段曲調就已經公開演奏過了。</p>
<p>椋鳥學得很快：維基百科的椋鳥條目提到，有人見過野生椋鳥只聽了一遍就模仿出某個聲音。在我們看來，最簡單的解釋是一位作曲家在鳥店裡用口哨吹著自己最新的曲子。但沒有任何文獻能證明真有其事。</p>

<h2 id="why-starlings-mimic">椋鳥為什麼這麼會模仿？</h2>
<p>歐洲椋鳥（<em>Sturnus vulgaris</em>）能透過學習掌握鳴聲，會把借來的聲音編進自己的鳴唱，所以寵物椋鳥學會一段口哨旋律，對這個物種來說再平常不過。雄鳥的一段鳴唱可以持續一分鐘甚至更久：先是純淨的哨音，接著是多變的樂段，夾雜著其他鳥的鳴聲片段和各種雜音，然後是一串喀嗒聲，最後以一陣高亢的鳴唱收尾。維基百科援引了 Chris Feare 1996 年在《<cite>British Birds</cite>》上對這個物種的介紹：經驗豐富的椋鳥最多能掌握 35 種多變的鳴唱類型。</p>
<p>Paul Ehrlich、David Dobkin 和 Darryl Wheye 在一篇談鳴聲模仿的文章中，把歐洲椋鳥列為以鳴聲模仿著稱的物種。他們的解釋是曲庫的大小：豐富多變的鳴唱能幫雄鳥吸引配偶、震懾對手，而從周圍環境借聲音——哪怕是狗叫、機器聲或人的口哨——就是擴充曲庫的一種辦法。他們還補充說，模仿者通常會因為自己的音色、節奏或場合不對而露出馬腳，所以聽者很少真被騙過。</p>
<p>寵物椋鳥很容易學會人說的話。維基百科援引康拉德·勞倫茲（Konrad Lorenz）的觀察：這些話對鳥來說毫無意義，所以它會把詞語混在一起，在莫名其妙的時候說出來。長期研究寵物椋鳥的 Meredith West 和 Andrew King 則把椋鳥的模仿能力與它們活躍的社交互動聯繫起來。</p>
<p>身體構造也幫了忙。2025 年發表在《<cite>Scientific Reports</cite>》上的一項研究，分析了網路上發布的 115 段鳥類模仿《<cite>星際大戰</cite>》機器人 R2-D2 的影片，其中 8 段是椋鳥。椋鳥模仿這個機器人複雜、多音調的聲音時比鸚鵡更準確，而模仿簡單的嗶嗶聲時兩者不相上下。作者把這種差異歸因於鳴管，也就是鳥類的發聲器官：椋鳥能分別控制鳴管的左右兩側，因此可以同時發出兩個聲音，鸚鵡則不能。</p>
<p>椋鳥聽曲調的方式似乎也和我們不一樣。2016 年發表在《<cite>PNAS</cite>》上的一項研究中，五隻椋鳥學會了區分音高上行和下行的四音音列。可一旦把音列整體稍微升高或降低，或者換一種音色播放，它們的表現就和亂猜差不多。相反，只要保留每個音的頻譜形狀（也就是能量在各個頻率上的分布），哪怕去掉音高，它們也能輕鬆得多地認出來。音樂家把旋律聽成一串音程，移調之後依然不變；椋鳥聽到的則更像是一串聲音的形狀。這也許正是椋鳥學唱的曲調很像、卻又不完全一樣的原因之一。這只是我們的解讀，並沒有人在莫札特那隻鳥身上測量過。</p>

<h2 id="the-funeral-and-the-poem">1787年6月：椋鳥的葬禮和莫札特的詩</h2>
<p>1787年6月4日，這隻椋鳥死了，距莫札特買下它大約三年。莫札特鄭重其事地把它葬在花園裡，還為它寫了一首詩。那是一段難熬的日子：一週前的 5月28日，他的父親利奧波德·莫札特剛在薩爾斯堡去世。</p>
<p>這場葬禮我們只能從二手資料中得知，主要來自莫札特的早期傳記作者，而且說法各不相同：</p>
<ul>
<li>採訪過莫札特遺孀康斯坦策（Constanze）的尼梅切克（Franz Xaver Niemetschek）提到，莫札特在他租來的花園裡，為一隻深愛的椋鳥立了一塊像樣的墓碑，上面刻有銘文。</li>
<li>康斯坦策的第二任丈夫尼森（Georg Nikolaus Nissen）在 1828 年出版的傳記裡也是這麼講的：一隻心愛的椋鳥死後，莫札特寫了詩，並在租來的花園裡為它修了一座像樣的墳，立了墓誌銘（第 668 頁）；他喜愛動物，尤其是鳥（第 670 頁）。送葬隊伍的說法則另有出處：Rudolf von Lewicki 於 1919年11月在《<cite>Mozarteums-Mitteilungen</cite>》上刊印的尼森札記輯錄。尼森在其中寫道，莫札特喜歡狗和鳥，家裡總養著幾隻，他的狗名叫 Puzzipaukel；每當有鳥死去，他就會安排一支送葬隊伍，凡是會唱歌的人都得披著大面紗跟在後面，還有一首類似安魂曲的歌和一首詩體墓誌銘（第 29 頁）。這條札記只說「一隻鳥」，並沒有點明是椋鳥；Otto Erich Deutsch 在他編的莫札特文獻傳記第 225 頁引用了這段話的英譯。</li>
<li>據 RILM 部落格的概述，West 和 King 1990 年發表在《<cite>American Scientist</cite>》上的文章描述的是：送葬者蒙著面紗列隊行進，唱著讚美詩，並在墓旁聆聽那首詩的朗誦。</li>
</ul>
<p>這首詩的落款是 1787年6月4日，開頭是「Hier ruht ein lieber Narr, / Ein Vogel Staar」（「這裡長眠著一個可愛的小傻瓜，一隻椋鳥」，我們的譯文）。詩裡哀悼這隻正值盛年就被奪走的鳥，堅稱它並不壞，只是活潑了些，有時是個惹人愛的小淘氣；結尾還拿作者自己開了個玩笑：鳥兒臨死時，根本沒想到那位「der so schön reimen kann」（押韻押得如此漂亮）的人。這裡的 schön，正是三年前莫札特寫在鳥兒曲調下方的那個詞。</p>
<p>把這首詩譯成英文的 Robert Spaethling 認為它既好笑又苦樂參半，寫於莫札特正處在哀痛之中、又埋頭創作《<cite>唐璜</cite>》的時候。民俗學家 Wolfgang Mieder 則認為，莫札特在某種程度上寫的是他自己。West 和 King 指出，寵物椋鳥和主人之間的感情非常深，這讓人很容易相信，詼諧背後確有真切的哀傷。</p>

<h2 id="did-the-starling-influence-mozart">椋鳥影響了莫札特的音樂嗎？</h2>
<p>協奏曲沒有受它影響——莫札特在買鳥的六週前就已經寫完了；至於說它影響了後來的《<cite>音樂玩笑</cite>》（<cite lang="de">Ein musikalischer Spaß</cite>，K. 522），這只是一個沒有文獻佐證的假說。博物學家、2017 年出版的《<cite>Mozart’s Starling</cite>》一書作者 Lyanda Lynn Haupt 對 NPR 說，從時間上看，「這隻鳥啟發了協奏曲」的說法可以排除。</p>
<p>提出後一種說法的是 West 和 King。他們在 1990 年的文章中寫道，《<cite>音樂玩笑</cite>》裡的許多元素「帶有椋鳥的聲音簽名」。時間點確實耐人尋味：1787年6月14日，也就是鳥死後十天，莫札特把這首作品登記進了作品目錄。Haupt 對 NPR 也提出了類似的看法：把這首曲子最古怪的段落和椋鳥的鳴唱疊在一起聽，用她的話說，能聽出同樣「支離破碎的樂句」和同樣的頑皮。</p>
<p>這只能算假說。維基百科指出，West 和 King 的依據是唱片封套說明裡對這首曲子的描述。許多評論者認為《<cite>音樂玩笑</cite>》是在戲仿蹩腳的作曲家，而目前沒有紀錄顯示莫札特說過這首曲子的用意。我們讀過的資料中，沒有一份引用過把這首曲子和那隻鳥聯繫起來的莫札特書信或筆記。就我們所知，莫札特和他的椋鳥之間唯一有文獻可考的音樂關聯，就是 1784年5月27日的那條帳本紀錄。</p>

<h2 id="why-we-named-scorestarling-after-it">我們為什麼用它給 ScoreStarling 命名</h2>
<p>我們用這隻鳥給產品命名，因為牠的故事正是我們在做的事。一句話概括：鳥兒在唱，作曲家把聽到的寫了下來。名字是 Score（樂譜）加 Starling（椋鳥），我們的標誌是一隻椋鳥，揚起的翅膀正好構成音符的符尾。那個不太為人所知的細節，對我們同樣重要：莫札特的那行譜忠實記下了他聽到的聲音，而他聽到的，和他自己寫的並不完全一樣。扒譜只是初稿，最後要靠耳朵來完成。</p>
<p>ScoreStarling 會聽錄音，把它寫成 MIDI 和可編輯的樂譜。你可以對照原錄音播放、重複播放某一段、修改音符，然後匯出。它也能讀取印刷樂譜、樂譜檔案以及 ABC 或 MusicXML 文字，並以五線譜、吉他六線譜或簡譜顯示。扒單一樂器、人聲或鋼琴獨奏以及編輯樂譜都是免費的；扒樂團、解鎖樂譜的 MusicXML 和 MIDI 需要消耗點數。每個帳號每月免費獲得 100 點，其餘細節見<a href="https://scorestarling.com/zh-hant/how-it-works#specs">規格說明</a>。我們的<a href="https://scorestarling.com/zh-hant/blog/ai-music-transcription-accuracy">準確率文章</a>寫明了我們測了什麼、用的是哪些錄音。</p>

<section class="sources" aria-labelledby="sources">
<h2 id="sources">參考資料</h2>
<ol>
<li><a href="https://dme.mozarteum.at/DME/objs/pdf/nma_153_-23_-3_eng.pdf">Neue Mozart-Ausgabe V/15/5，Eva 和 Paul Badura-Skoda 撰寫的前言（1965），英譯本</a>——Internationale Stiftung Mozarteum，Digital Mozart Edition</li>
<li><a href="https://dme.mozarteum.at/DME/nma/nma_cont.php?vsep=153&amp;gen=edition&amp;l=2&amp;p1=46">Neue Mozart-Ausgabe V/15/5：G 大調協奏曲 K. 453 第三樂章，樂譜第 46 頁</a>——Internationale Stiftung Mozarteum，Digital Mozart Edition</li>
<li><a href="https://kv.mozarteum.at/en/work/ein-musikalischer-spass-a-musical-joke-for-2-horns-2-violins-viola-and-basso-6036">KV 522, Ein musikalischer Spaß</a>——克歇爾目錄線上版，Internationale Stiftung Mozarteum</li>
<li><a href="https://en.wikipedia.org/wiki/Mozart%27s_starling">Mozart’s starling</a>——英文維基百科（引用 Deutsch 1965、Heartz 2009、Abert 2016、West 和 King 1990、Spaethling 2000、Mieder 2003 及 Niemetschek）</li>
<li><a href="https://en.wikipedia.org/wiki/Piano_Concerto_No._17_(Mozart)">Piano Concerto No. 17 (Mozart)</a>——英文維基百科（引用 Lorenz 2006）</li>
<li><a href="https://en.wikipedia.org/wiki/Common_starling">Common starling</a>——英文維基百科（引用 Feare 1996，載於《<cite>British Birds</cite>》；Cocker 和 Mabey 2005；Lorenz 1961）</li>
<li><a href="https://en.wikipedia.org/wiki/A_Musical_Joke">A Musical Joke</a>——英文維基百科</li>
<li><a href="https://en.wikipedia.org/wiki/Leopold_Mozart">Leopold Mozart</a>——英文維基百科</li>
<li><a href="https://en.wikipedia.org/wiki/Kreuzer">Kreuzer</a>——英文維基百科</li>
<li><a href="https://digibib.mozarteum.at/ismretroverbund/periodical/pageview/2498147">Aus Nissens Kollektaneen</a>——Rudolf von Lewicki，《<cite>Mozarteums-Mitteilungen</cite>》第 2 卷第 1 期，薩爾斯堡，1919年11月，第 28–30 頁（掃描檔見國際莫札特基金會的 Bibliotheca Mozartiana digital）</li>
<li><a href="https://books.google.com/books?id=e8AtwaddUW4C&amp;pg=PA225">Mozart: A Documentary Biography</a>——Otto Erich Deutsch，Stanford University Press，1966 年版，第 225 頁（引用 Rudolf von Lewicki 刊印於《<cite>Mozarteums-Mitteilungen</cite>》1919年11月號的尼森札記）</li>
<li><a href="https://archive.org/details/bub_gb_BRRbAAAAQAAJ">Biographie W. A. Mozart’s</a>——Georg Nikolaus von Nissen，Leipzig: Breitkopf &amp; Härtel，1828，第 668 頁和第 670 頁（掃描檔見網際網路檔案館）</li>
<li><a href="https://bibliolore.org/2017/09/14/mozarts-starling/">Mozart’s starling</a>——Bibliolore，RILM 部落格（2017），概述 West 和 King 的「Mozart’s Starling」，載於《<cite>American Scientist</cite>》第 78 卷第 2 期（1990）：106–114</li>
<li><a href="https://www.npr.org/sections/deceptivecadence/2017/04/20/524349771/how-do-you-bond-with-mozart-adopt-a-starling">How Do You Bond With Mozart? Adopt A Starling</a>——Tom Huizenga，NPR（2017年4月20日）</li>
<li><a href="https://web.stanford.edu/group/stanfordbirds/text/uessays/uVocal_Copying.html">Vocal Copying</a>——Paul R. Ehrlich、David S. Dobkin 和 Darryl Wheye，Birds of Stanford（1988）</li>
<li><a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC12592491/">What imitating an iconic robot reveals on allospecific vocal imitation in parrots and starlings</a>——Nick C. P. Dam、Henkjan Honing 和 Michelle J. Spierings，《<cite>Scientific Reports</cite>》（2025）</li>
<li><a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC4760803/">Songbirds use spectral shape, not pitch, for sound pattern recognition</a>——Micah R. Bregman、Aniruddh D. Patel 和 Timothy Q. Gentner，《<cite>PNAS</cite>》第 113 卷（2016）</li>
</ol>
</section><h2>常見問題</h2><h3>莫札特的椋鳥唱的是哪首曲子？</h3><p>莫札特 G 大調第十七號鋼琴協奏曲（K. 453）終樂章（Allegretto，小快板）主題的第一個樂句。莫札特在 1784年4月12日把這首協奏曲登記進作品目錄；5月27日買下這隻鳥時，他在帳本上記下了它唱的版本：同樣是一個弱起加四個小節，只是第 1 小節末尾的 G 上多了一個停頓，第 2 小節的 G 唱成了升 G。</p><h3>莫札特帳本上的「Das war schön」是什麼意思？</h3><p>意思是「真美啊」或「真好聽」；《新莫札特全集》（Neue Mozart-Ausgabe）前言的英譯本譯作「That was nice!」（真不錯！）。1784年5月27日，莫札特在帳本上記下這隻鳥（「Vogel Stahrl」）和它的價錢 34 克羅伊策，並把這句話寫在椋鳥版本的曲調下方。</p><h3>莫札特的椋鳥後來怎麼樣了？</h3><p>它在莫札特家生活了大約三年，1787年6月4日死去，比莫札特的父親利奧波德晚走一週。莫札特把它葬在花園裡，還為它寫了一首打油詩，落款就是這一天，開頭是「Hier ruht ein lieber Narr, / Ein Vogel Staar」（「這裡長眠著一個可愛的小傻瓜，一隻椋鳥」）。莫札特的早期傳記作者尼森（Georg Nikolaus Nissen）寫道，莫札特在租來的花園裡為這隻心愛的椋鳥修了一座像樣的墳墓，立了墓誌銘，還說莫札特喜愛動物，尤其是鳥；至於蒙著面紗的送葬者，則出自尼森的私人筆記，1919 年才首次刊印，並不在那本傳記裡。</p><h3>椋鳥真的會學唱曲子嗎？</h3><p>會。歐洲椋鳥擅長模仿，會把別的鳥的鳴唱和其他聲音編進自己的歌裡；養在家裡的椋鳥也會跟身邊的人學說話、學曲調。它們學得很像，但不完全一樣：莫札特的鳥多了一個停頓，還把 G 唱成了升 G；2016 年的一項實驗室研究發現，椋鳥靠頻譜形狀而不是音高來辨認音列。</p>]]></content:encoded>
    </item>
  </channel>
</rss>
