瀏覽代碼

Add verovio

Use verovio for editing MusicXML files.
master
TheoryOfNekomata 7 月之前
父節點
當前提交
61a0b914ab
共有 8 個檔案被更改,包括 4462 行新增174 行删除
  1. +7
    -1
      package.json
  2. +315
    -15
      pnpm-lock.yaml
  3. +2669
    -0
      src/assets/haydn-piano-sonata-6-slice00.musicxml
  4. +1342
    -0
      src/assets/haydn-piano-sonata-6-slice01.musicxml
  5. +0
    -153
      src/components/Notation/index.tsx
  6. +2
    -0
      src/layouts/Layout.astro
  7. +118
    -0
      src/pages/api/assets/[asset].svg.ts
  8. +9
    -5
      src/pages/ch20-trills.mdx

+ 7
- 1
package.json 查看文件

@@ -17,10 +17,16 @@
"@types/react": "^18.2.74",
"@types/react-dom": "^18.2.24",
"astro": "^4.5.16",
"jsdom": "^24.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.4.3",
"typescript": "^5.4.4",
"vexflow": "5.0.0-alpha.3"
"verovio": "^4.1.0"
},
"devDependencies": {
"@types/jsdom": "^21.1.6",
"@types/node": "^20.12.7",
"@types/verovio": "^3.13.4"
}
}

+ 315
- 15
pnpm-lock.yaml 查看文件

@@ -25,7 +25,10 @@ dependencies:
version: 18.2.24
astro:
specifier: ^4.5.16
version: 4.5.16(typescript@5.4.4)
version: 4.5.16(@types/node@20.12.7)(typescript@5.4.4)
jsdom:
specifier: ^24.0.0
version: 24.0.0
react:
specifier: ^18.2.0
version: 18.2.0
@@ -38,9 +41,20 @@ dependencies:
typescript:
specifier: ^5.4.4
version: 5.4.4
vexflow:
specifier: 5.0.0-alpha.3
version: 5.0.0-alpha.3
verovio:
specifier: ^4.1.0
version: 4.1.0

devDependencies:
'@types/jsdom':
specifier: ^21.1.6
version: 21.1.6
'@types/node':
specifier: ^20.12.7
version: 20.12.7
'@types/verovio':
specifier: ^3.13.4
version: 3.13.4

packages:

@@ -148,7 +162,7 @@ packages:
'@astrojs/markdown-remark': 5.0.0
'@mdx-js/mdx': 3.0.1
acorn: 8.11.3
astro: 4.5.16(typescript@5.4.4)
astro: 4.5.16(@types/node@20.12.7)(typescript@5.4.4)
es-module-lexer: 1.5.0
estree-util-visit: 2.0.0
github-slugger: 2.0.0
@@ -198,7 +212,7 @@ packages:
astro: ^3.0.0 || ^4.0.0
tailwindcss: ^3.0.24
dependencies:
astro: 4.5.16(typescript@5.4.4)
astro: 4.5.16(@types/node@20.12.7)(typescript@5.4.4)
autoprefixer: 10.4.19(postcss@8.4.38)
postcss: 8.4.38
postcss-load-config: 4.0.2(postcss@8.4.38)
@@ -1181,6 +1195,14 @@ packages:
'@types/unist': 3.0.2
dev: false

/@types/jsdom@21.1.6:
resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
dependencies:
'@types/node': 20.12.7
'@types/tough-cookie': 4.0.5
parse5: 7.1.2
dev: true

/@types/mdast@4.0.3:
resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
dependencies:
@@ -1201,6 +1223,11 @@ packages:
'@types/unist': 2.0.10
dev: false

/@types/node@20.12.7:
resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==}
dependencies:
undici-types: 5.26.5

/@types/prop-types@15.7.12:
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
dev: false
@@ -1218,6 +1245,10 @@ packages:
csstype: 3.1.3
dev: false

/@types/tough-cookie@4.0.5:
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
dev: true

/@types/unist@2.0.10:
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
dev: false
@@ -1226,6 +1257,10 @@ packages:
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
dev: false

/@types/verovio@3.13.4:
resolution: {integrity: sha512-IM4AGwbnbsn9IF72Z2Vy2mcCCEJZobtZjd9XyB6VovhZlJoUbW9m34X0twW0NgID+tVhbP6s1rMphXkoy4c6qQ==}
dev: true

/@ungap/structured-clone@1.2.0:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
dev: false
@@ -1241,7 +1276,7 @@ packages:
'@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.4)
'@types/babel__core': 7.20.5
react-refresh: 0.14.0
vite: 5.2.8
vite: 5.2.8(@types/node@20.12.7)
transitivePeerDependencies:
- supports-color
dev: false
@@ -1342,6 +1377,15 @@ packages:
hasBin: true
dev: false

/agent-base@7.1.1:
resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
engines: {node: '>= 14'}
dependencies:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: false

/ansi-align@3.0.1:
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
dependencies:
@@ -1418,7 +1462,7 @@ packages:
hasBin: true
dev: false

/astro@4.5.16(typescript@5.4.4):
/astro@4.5.16(@types/node@20.12.7)(typescript@5.4.4):
resolution: {integrity: sha512-1nOVsMq2OJiXnG6gO0Y77vTAboGN9nLQSy/8SGazq4h6x+alzbsMbQbArBgvaLzOSUXD0m91XLs3D8bOSuavrQ==}
engines: {node: '>=18.14.1', npm: '>=6.14.0'}
hasBin: true
@@ -1479,7 +1523,7 @@ packages:
tsconfck: 3.0.3(typescript@5.4.4)
unist-util-visit: 5.0.0
vfile: 6.0.1
vite: 5.2.8
vite: 5.2.8(@types/node@20.12.7)
vitefu: 0.2.5(vite@5.2.8)
which-pm: 2.1.1
yargs-parser: 21.1.1
@@ -1499,6 +1543,10 @@ packages:
- typescript
dev: false

/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false

/autoprefixer@10.4.19(postcss@8.4.38):
resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==}
engines: {node: ^10 || ^12 || >=14}
@@ -1805,6 +1853,13 @@ packages:
dev: false
optional: true

/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: false

/comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
dev: false
@@ -1842,10 +1897,25 @@ packages:
hasBin: true
dev: false

/cssstyle@4.0.1:
resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==}
engines: {node: '>=18'}
dependencies:
rrweb-cssom: 0.6.0
dev: false

/csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
dev: false

/data-urls@5.0.0:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
dependencies:
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
dev: false

/debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
@@ -1858,6 +1928,10 @@ packages:
ms: 2.1.2
dev: false

/decimal.js@10.4.3:
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
dev: false

/decode-named-character-reference@1.0.2:
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
dependencies:
@@ -1880,6 +1954,11 @@ packages:
dev: false
optional: true

/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: false

/dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@@ -1965,7 +2044,6 @@ packages:
/entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
dev: false

/es-module-lexer@1.5.0:
resolution: {integrity: sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==}
@@ -2197,6 +2275,15 @@ packages:
signal-exit: 4.1.0
dev: false

/form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false

/fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
dev: false
@@ -2459,6 +2546,13 @@ packages:
space-separated-tokens: 2.0.2
dev: false

/html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
dependencies:
whatwg-encoding: 3.1.1
dev: false

/html-escaper@3.0.3:
resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==}
dev: false
@@ -2471,11 +2565,38 @@ packages:
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
dev: false

/http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.1
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: false

/https-proxy-agent@7.0.4:
resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.1
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: false

/human-signals@5.0.0:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'}
dev: false

/iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
dev: false

/ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: false
@@ -2596,6 +2717,10 @@ packages:
engines: {node: '>=12'}
dev: false

/is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
dev: false

/is-reference@3.0.2:
resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==}
dependencies:
@@ -2656,6 +2781,42 @@ packages:
argparse: 2.0.1
dev: false

/jsdom@24.0.0:
resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==}
engines: {node: '>=18'}
peerDependencies:
canvas: ^2.11.2
peerDependenciesMeta:
canvas:
optional: true
dependencies:
cssstyle: 4.0.1
data-urls: 5.0.0
decimal.js: 10.4.3
form-data: 4.0.0
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.8
parse5: 7.1.2
rrweb-cssom: 0.6.0
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 4.1.3
w3c-xmlserializer: 5.0.0
webidl-conversions: 7.0.0
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
ws: 8.16.0
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: false

/jsesc@2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
@@ -3332,6 +3493,18 @@ packages:
picomatch: 2.3.1
dev: false

/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: false

/mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.52.0
dev: false

/mime@3.0.0:
resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
engines: {node: '>=10.0.0'}
@@ -3449,6 +3622,10 @@ packages:
path-key: 4.0.0
dev: false

/nwsapi@2.2.8:
resolution: {integrity: sha512-GU/I3lTEFQ9mkEm07Q7HvdRajss8E1wVMGOk3/lHl60QPseG+B3BIQY+JUjYWw7gF8cCeoQCXd4N7DB7avw0Rg==}
dev: false

/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -3574,7 +3751,6 @@ packages:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
dependencies:
entities: 4.5.0
dev: false

/path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
@@ -3768,6 +3944,10 @@ packages:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
dev: false

/psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
dev: false

/pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
requiresBuild: true
@@ -3777,6 +3957,15 @@ packages:
dev: false
optional: true

/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
dev: false

/querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
dev: false

/queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: false
@@ -3945,6 +4134,10 @@ packages:
engines: {node: '>=0.10.0'}
dev: false

/requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
dev: false

/resolve@1.22.8:
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
hasBin: true
@@ -4027,6 +4220,10 @@ packages:
fsevents: 2.3.3
dev: false

/rrweb-cssom@0.6.0:
resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
dev: false

/run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:
@@ -4037,6 +4234,17 @@ packages:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: false

/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: false

/saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
dependencies:
xmlchars: 2.2.0
dev: false

/scheduler@0.23.0:
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
dependencies:
@@ -4294,6 +4502,10 @@ packages:
engines: {node: '>= 0.4'}
dev: false

/symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
dev: false

/tailwindcss@3.4.3:
resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==}
engines: {node: '>=14.0.0'}
@@ -4396,6 +4608,23 @@ packages:
is-number: 7.0.0
dev: false

/tough-cookie@4.1.3:
resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
engines: {node: '>=6'}
dependencies:
psl: 1.9.0
punycode: 2.3.1
universalify: 0.2.0
url-parse: 1.5.10
dev: false

/tr46@5.0.0:
resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
engines: {node: '>=18'}
dependencies:
punycode: 2.3.1
dev: false

/trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
dev: false
@@ -4454,6 +4683,9 @@ packages:
resolution: {integrity: sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==}
dev: false

/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}

/unherit@3.0.1:
resolution: {integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==}
dev: false
@@ -4575,6 +4807,11 @@ packages:
unist-util-visit-parents: 6.0.1
dev: false

/universalify@0.2.0:
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
engines: {node: '>= 4.0.0'}
dev: false

/update-browserslist-db@1.0.13(browserslist@4.23.0):
resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
hasBin: true
@@ -4586,12 +4823,20 @@ packages:
picocolors: 1.0.0
dev: false

/url-parse@1.5.10:
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
dependencies:
querystringify: 2.2.0
requires-port: 1.0.0
dev: false

/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: false

/vexflow@5.0.0-alpha.3:
resolution: {integrity: sha512-Los8mo4YI1xNYxrD9OfRheoc7nhI9ynQ3pP6Sj58gG0I+AOUPkKBoWtHYTm+XqBgtv0AzhkPAKVjuLu4voI5Fw==}
/verovio@4.1.0:
resolution: {integrity: sha512-HjSMVOlpOeK73mLEF/QVG/2UpFI3hvoGF0p324XwD+/qlcVdfJbJhic4FnIFJGxOcH4WNo0WVEjKP6R93VY/ew==}
engines: {node: '>= 14.18.0'}
dev: false

/vfile-location@5.0.2:
@@ -4632,7 +4877,7 @@ packages:
vfile-message: 4.0.2
dev: false

/vite@5.2.8:
/vite@5.2.8(@types/node@20.12.7):
resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
@@ -4660,6 +4905,7 @@ packages:
terser:
optional: true
dependencies:
'@types/node': 20.12.7
esbuild: 0.20.2
postcss: 8.4.38
rollup: 4.14.1
@@ -4675,7 +4921,7 @@ packages:
vite:
optional: true
dependencies:
vite: 5.2.8
vite: 5.2.8(@types/node@20.12.7)
dev: false

/volar-service-css@0.0.34(@volar/language-service@2.1.6):
@@ -4818,10 +5064,42 @@ packages:
resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==}
dev: false

/w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
dependencies:
xml-name-validator: 5.0.0
dev: false

/web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
dev: false

/webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
dev: false

/whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
dependencies:
iconv-lite: 0.6.3
dev: false

/whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
dev: false

/whatwg-url@14.0.0:
resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
engines: {node: '>=18'}
dependencies:
tr46: 5.0.0
webidl-conversions: 7.0.0
dev: false

/which-pm-runs@1.1.0:
resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==}
engines: {node: '>=4'}
@@ -4882,6 +5160,28 @@ packages:
dev: false
optional: true

/ws@8.16.0:
resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: false

/xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
dev: false

/xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
dev: false

/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}


+ 2669
- 0
src/assets/haydn-piano-sonata-6-slice00.musicxml
文件差異過大導致無法顯示
查看文件


+ 1342
- 0
src/assets/haydn-piano-sonata-6-slice01.musicxml
文件差異過大導致無法顯示
查看文件


+ 0
- 153
src/components/Notation/index.tsx 查看文件

@@ -1,153 +0,0 @@
import * as React from 'react';
import * as Vexflow from 'vexflow';

export const Notation = () => {
const ref = React.useRef<HTMLElementTagNameMap['div']>(null);
const id = React.useId();

React.useEffect(() => {
const render = (id: string) => {
const el = window.document.getElementById(id);
if (!el) {
return;
}

el.innerHTML = '';
const { Factory } = Vexflow;
const width = el.clientWidth;
const vf = new Factory({
renderer: { elementId: id, width, height: 300 },
});

vf.getContext().scale(0.75, 0.75);

const score = vf.EasyScore();
let currentX = 20;
let currentY = 40;

const getCurrentBar = (width: number) => {
const system = vf.System({
x: currentX,
y: currentY,
width,
});
currentX += width;
return system;
};

let system = getCurrentBar(130);
system.addStave({
voices: [
score.voice([
...score.notes('D5/8'),
], {
time: '1/8',
})
]
})
.addClef('treble')
.addKeySignature('Gm')
.addTimeSignature('C')
.setTempo(
{
name: 'Adagio.'
},
-30
);

system.addStave({
voices: [
score.voice([
...score.notes('D3/8/r', { clef: 'bass' }),
], {
time: '1/8',
})
]
})
.addClef('bass')
.addKeySignature('Gm')
.addTimeSignature('C');

system.addConnector('brace');
system.addConnector('singleLeft');
system.addConnector('singleRight');

system = getCurrentBar(350);

system
.addStave({
voices: [
score.voice(
[
...score.tuplet(
score.beam(
score.notes('G5/16, B5, A5, G5, F#5, G5'),
),
{
ratioed: false,
notesOccupied: 4,
}
),
...score.tuplet(
score.beam(
score.notes('G5/16, F5, G5, D6, F5, G5'),
),
{
ratioed: false,
notesOccupied: 4,
yOffset: Infinity,
}
),
// ...score.beam(score.notes('E5/8, D5')),
...score.beam(score.notes('E5/8, D5')),
...score.tuplet(
score.beam(
score.notes('D5/16, E5, F5, G5, A5, B5'),
),
{
ratioed: false,
notesOccupied: 4,
yOffset: Infinity,
}
),
]
),
]
});

system
.addStave({
voices: [
score.voice([
...score.beam(
score.notes('(G3 B3 D4)/8, (G3 B3 D4)', { clef: 'bass' })
),
...score.beam(
score.notes('(G3 B3 D4)/8, (G3 B3 D4)', { clef: 'bass' })
),
...score.beam(
score.notes('(G3 C4)/8, (G3 B3)', { clef: 'bass' })
),
...score.beam(
score.notes('(G3 B3)/8, (G3 B3)', { clef: 'bass' })
),
]),
]
});

system.addConnector('singleLeft');
system.addConnector('singleRight');

vf.draw();
};

render(id);
}, []);

return (
<div
ref={ref}
id={id}
/>
);
};

+ 2
- 0
src/layouts/Layout.astro 查看文件

@@ -12,6 +12,8 @@ const { title } = Astro.props.frontmatter || Astro.props;
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<style is:global>
@import url("https://www.verovio.org/javascript/4.1.0/data/Bravura.css");

p {
@apply my-6;
@apply indent-6;


+ 118
- 0
src/pages/api/assets/[asset].svg.ts 查看文件

@@ -0,0 +1,118 @@
import createVerovioModule from 'verovio/wasm';
import { VerovioToolkit } from 'verovio/esm';
import { readFile, readdir } from 'node:fs/promises';
import { JSDOM } from 'jsdom';
import type {APIRoute, GetStaticPaths} from 'astro';

const filter = (musicXml: string) => {
const jsdom = new JSDOM(musicXml, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
const win = jsdom.window;
// const win = jsdom.window;
return win.document.documentElement.outerHTML;
};

const processOutput = (xmlData: string) => {
const jsdom = new JSDOM(xmlData, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
const win = jsdom.window;

const [svgElemsRoot, svgElemsMain] = Array.from(win.document.getElementsByTagName('svg'));
if (typeof svgElemsRoot === 'undefined') {
return '';
}

if (typeof svgElemsMain === 'undefined') {
return '';
}

// svgElemsRoot.getAttributeNames().forEach((a) => {
// const attr = svgElemsRoot.getAttribute(a);
// if (!attr) {
// return;
// }
// svgElemsMain.setAttribute(a, attr);
// });

Array.from(svgElemsRoot.children).forEach((h) => {
if (h !== svgElemsMain) {
h.remove();
if (h.tagName.toLowerCase() === 'desc') {
return;
}
if (svgElemsMain.children[0]) {
svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
return;
}
svgElemsMain.appendChild(h);
}
});

Array.from(win.document.getElementsByClassName('pgHead')).forEach(h => {
h.remove();
});

Array.from(win.document.getElementsByClassName('pgFoot')).forEach(h => {
h.remove();
});

Array.from(win.document.getElementsByTagName('defs')).forEach(h => {
h.remove();
if (svgElemsMain) {
if (svgElemsMain.children[0]) {
svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
} else {
svgElemsMain.appendChild(h);
}
}
});

return `<?xml version="1.0" encoding="utf-8"?>${svgElemsMain.outerHTML}`
};

export const GET: APIRoute = async ({ params }) => {
const verovioModule = await createVerovioModule();
verovioModule.FS_createDataFile('', 'bravura', 'asdfasdf', true, false, true);
const score = await readFile(`src/assets/${params.asset}.musicxml`, 'utf-8');
const verovioToolkit = new VerovioToolkit(verovioModule);
const filteredScore = filter(score);
const isSuccessful = verovioToolkit.loadData(filteredScore);
if (!isSuccessful) {
return new Response(null, { status: 500 });
}

verovioToolkit.setOptions({
breaks: 'none',
font: 'Bravura',
});

let data: string;
try {
const raw = verovioToolkit.renderToSVG(1)
.replace(/xmlns:mei="(.+?)"/g, '')
.replace(/xlink:/g, '');
data = processOutput(raw);
} catch (err) {
console.error(err);
return new Response(null, { status: 500 });
}

return new Response(
data,
{
headers: {
'Content-Type': 'image/svg+xml',
},
status: 200,
}
);
};

export const getStaticPaths: GetStaticPaths = async () => {
const files = await readdir('src/assets');
return files
.filter((f) => f.endsWith('.musicxml'))
.map((f) => ({
params: {
asset: f.replace(/\.musicxml/g, ''),
},
}));
};

+ 9
- 5
src/pages/ch20-trills.mdx 查看文件

@@ -3,16 +3,20 @@ title: Trills
layout: ../layouts/Layout.astro
---

import {Notation} from '../components/Notation';

I have attended the session where I was supposed to demonstrate the trills I had been working for the past few days. I
started with the third movement of Haydn Piano Sonata No. 6:

<Notation client:visible />
<img src="/api/assets/haydn-piano-sonata-6-slice01.svg" alt="" />

Sir demonstrated to me where the "Adagio" is meant for, where the pulse is made out of quarter notes. He gestured to me
the 4/4 time signature and said the grouping is made out of 6 notes "imagine the pulse is made out of sextuplets".
Sir demonstrated to me where the "Adagio" is meant for, as the pulse is made out of quarter notes. He gestured to me
the 4/4 time signature and said the grouping is made out of 6 notes--"imagine the pulse is made out of sextuplets".

He said my trills were "ugly", to which I agreed. I was still struggling to make the trills "sound right". He suggested
I "count" the trills. However, that felt too much for me to handle, so I tried clinging to my sensation. I used my
(2) to "count", i.e. align my trills to the measure.

He asked me how did my experience compare with playing measured trills--I said I used to play instinctively. He added
that playing trills instinctively could be detrimental when used in every situation. He asked if I was aiming to play
trills as to "excite" the listener, of which the pieces I played (mostly from the Romantic era) did have dramatic
trills. I realized that the manner of playing trills will depend on the style of music--measured trills would be
beneficial to a piece in the Classical period like Haydn's.

Loading…
取消
儲存