MuseScore app-web fork
Tận dụng UI + editor + engraving trong Qt/WASM; thêm MXL và API cho practice. Phù hợp khi mô hình phân phối chấp nhận nghĩa vụ GPLv3.
Xây Musix thành một notation studio trên web dùng lõi MuseScore cho import, edit và engraving; sau đó dùng cùng một score để luyện piano MIDI, luyện hát, playback theo đoạn và phân tích hiệu suất.
Một canonical score, một engraving engine, nhiều trải nghiệm học nhạc.
Đây là đường ngắn nhất đến import chất lượng, full edit và render nhất quán vì MuseScore đã đưa DOM, transaction/undo, notation interaction, layout và Qt UI vào cấu hình WebAssembly. Không cần viết lại một editor cấp MuseScore bằng TypeScript.
Tận dụng UI + editor + engraving trong Qt/WASM; thêm MXL và API cho practice. Phù hợp khi mô hình phân phối chấp nhận nghĩa vụ GPLv3.
Import/render/edit ở tiến trình hoặc dịch vụ riêng, web nhận ảnh/geometry/state. Dùng nếu bundle WASM hoặc browser performance không đạt; vẫn cần rà soát license.
Chỉ chọn nếu GPL không phù hợp. Chi phí lớn nhất nằm ở invariant, layout, hit-testing, spanner, round-trip và hàng nghìn edge case—not ở việc vẽ nốt.
Musix không chỉ “xem sheet nhạc”; nó biến một score thành môi trường soạn, luyện và phản hồi có thể thích ứng.
Nhập .mxl/.musicxml/.xml, render đúng engraving, playback và xuất lại.
Sửa trực tiếp score hoặc tạo mới: note input, structure, text, layout, parts và style.
Kết nối đàn điện, tập theo đoạn, chờ đúng nốt, chấm pitch/rhythm và theo dõi tiến bộ.
Hiển thị line/block theo cao độ; so đường hát của user với target bằng cents và timing.
Phân tích mã nguồn hiện tại làm thay đổi đáng kể chiến lược: upstream đã có đường web chính thức, nhưng chưa phải một SDK ổn định cho bên thứ ba.
Workflow dùng Qt wasm_singlethread và cấu hình app-web. Engraving, notation, project, properties, palette, playback và UI shell đều được bật. QML web shell đã nhúng NotationView, note-input bar, palette, layout và properties—đây là editor thật, không chỉ viewer.
MUE_BUILD_IMPEXP_MUSICXML_MODULE=OFF ở preset web. Image export, MIDI và converter cũng đang tắt. Đây là khoảng trống có ranh giới rõ để Musix bật lại từng module hoặc thay bằng bridge/server và đo bundle/runtime.
Chỉ export các hàm load, start audio và add soundfont. Hàm load ghi bytes vào /mu/temp/current.mscz, nên chưa nhận diện MXL.
NotationInteraction, NotationNoteInput, InputState và transaction manager bao phủ note input, voice, tuplet, text, measure, transpose, selection, undo và hơn nữa.
Reader mở ZIP, đọc META-INF/container.xml và chạy parser hai pass: pass 1 dựng structure/time/voice; pass 2 tạo notation chi tiết.
ScoreChanges lưu tick/staff range; layout có layoutRange(start, end); paint đi qua cùng geometry engine nên selection/hit-test có thể đồng bộ với hình.
src/engraving
Score DOM, editing, transaction, layout, drawing, SMuFL font.
Core
src/notation
Interaction facade, note input, selection, painting, undo, playback interface.
Core
src/notationscene
Actions/commands, controllers và Qt/QML notation UI.
Spike
src/importexport/musicxml
MusicXML/MXL reader, writer, two-pass import và tests.
Enable
src/web/appjs
Bridge JavaScript hiện có; điểm mở rộng load/export/events.
Extend
buildscripts/ci/wasm
Build pipeline chính thức cho Emscripten/Qt WebAssembly.
Reuse
Engine sở hữu score; UI web điều phối workflow và practice nhưng không chỉnh trực tiếp XML hay DOM nội bộ.
EngravingItem là base element; tie, slur, hairpin và các đường kéo dài là Spanner. Stable EID được dùng để nối score element với overlay/practice, không dùng page index hay tọa độ SVG làm identity.
MasterScore + revision + undo stack. Tất cả mutation đi qua MuseScore command/interaction layer trong một transaction.
Layout pages, playback event list, PracticeIR, thumbnail và search index có thể hủy rồi dựng lại từ canonical score.
Autosave canonical MSCZ/snapshot + command journal. Giữ original MXL và ImportHealthReport để audit/round-trip.
Import là biên chuyển đổi có diagnostics; không parse lại XML sau mỗi thao tác và không hứa round-trip byte-identical.
Part/instrument, staff mapping, measure length, divisions, voice allocation, clef, key/time signature, transposition và page/system breaks. Mục tiêu là dựng timeline hợp lệ trước khi tạo chi tiết notation.
Chord/rest/note, tie/slur, tuplet, beam, lyric, dynamic, harmony, pedal, glissando, volta, tremolo và các direction/spanner khác.
| Trạng thái | Ý nghĩa | Hành vi khi save | Hiển thị cho user |
|---|---|---|---|
| Editable | Engine hiểu, render và edit đầy đủ. | Serialize theo state mới. | Cho phép mọi command đã cam kết. |
| Preserved | Chưa edit nhưng có thể bảo toàn source payload/semantics. | Pass-through có điều kiện, kèm test. | Read-only + giải thích. |
| Normalized | Ý nghĩa giữ nguyên nhưng layout/encoding đổi. | Xuất representation chuẩn của Musix/MuseScore. | Thông báo trước khi export. |
| Unsupported | Không thể bảo toàn an toàn. | Chặn save hoặc yêu cầu xác nhận rõ. | Diagnostic cụ thể; không silent loss. |
“Full” là một support contract theo tầng, không phải checkbox. Mỗi element phải có trạng thái hỗ trợ và test corpus tương ứng.
| Mức | Khả năng | Ưu tiên | Exit condition |
|---|---|---|---|
| L0 · Fidelity | Import, render, select, playback, save/export không mất dữ liệu đã cam kết. | Đầu tiên | Semantic + visual regression pass. |
| L1 · Composer core | Note/rest/chord, duration, voice, accidental, tie, measure, signature, clipboard. | P0 | Soạn được piano/vocal score thông dụng. |
| L2 · Structural | Tuplet, beam, slur, lyric, harmony, dynamics, repeat, volta, instrument/staff. | P1 | Corpus ensemble phổ biến không silent loss. |
| L3 · Publication | Spanner, ornaments, page/system breaks, styles, frames, linked parts. | P1–P2 | Layout/parts support contract hoàn chỉnh. |
| L4 · Specialist | Cross-staff, percussion, tablature, microtonal, figured bass, ossia, custom symbol. | P2 | Fixture chuyên ngành + round-trip pass. |
// TypeScript không mutate Score DOM trực tiếp.
applyCommand({
commandId: "cmd_01J...",
baseRevision: 184,
type: "notation.changeDuration",
targetIds: ["eid_8472"],
payload: { fraction: "1/8", dots: 1 }
})
// Engine bắt đầu transaction, chạy MuseScore interaction, validate,
// commit hoặc rollback; sau đó trả projection delta.
⇒ {
revision: 185,
changedIds: ["eid_8472"],
invalidatedRanges: [{ tick: [960, 1440], staff: [0, 1] }],
diagnostics: []
}
MuseScore có LIST selection và RANGE [startSegment,endSegment) × [staffStart,staffEnd). Transaction snapshot cả selection và InputState, nên bridge không được tự dựng selection model thứ hai.
Note, duration, measure, tie, voice và paste phải đi qua engraving/editing. Direct property change chỉ dành cho inspector property mà command contract cho phép.
UndoStack/TransactionManager trong core là authority. Web chỉ gửi baseRevision + commandId; không tạo thêm JS undo stack cho score.
Renderer không chỉ trả ảnh. Nó phải trả đúng geometry cho caret, selection, hit-test, drag và practice overlay.
Giữ adapter nhỏ để theo upstream dễ hơn. Không expose con trỏ DOM nội bộ hoặc mirror toàn bộ score sang JavaScript.
loadNamed(bytes, "lesson.mxl")
newScore({ title, instruments })
saveSnapshot("mscz")
exportScore("mxl" | "pdf" | "audio") // capability-gated
dispatchAction(actionCode, payload)
undo() / redo()
setSelection(elementIds)
setLoop(tickStart, tickEnd)
getImportHealth()
getPracticeIR(revision, range)
on("revision|selection|playback|saved", fn)
| Thành phần | Owner | Không nên làm |
|---|---|---|
| Score mutation + invariant | MuseScore engine | JS sửa XML, duration hoặc link thủ công. |
| Layout + hit-test | MuseScore engraving/scene | Dùng renderer thứ hai làm canonical. |
| Workflow + account + lesson | Musix web shell | Nhét toàn bộ product logic vào fork C++. |
| Practice scoring | Musix Practice Engine | Dùng pixel/canvas làm dữ liệu âm nhạc. |
| Persistence | Musix orchestration + engine serializer | Chỉ lưu ảnh hoặc MusicXML sau mỗi edit. |
Editor là hạ tầng; practice là khác biệt sản phẩm. Cả piano và vocal đều dùng range, tempo map, voice và stable element ID từ score revision hiện tại.
Read model gồm EID, part/staff/voice/track, measure, tick/duration dạng phân số chính xác, written/sounding/MIDI pitch, repeat occurrence, playback time, lyric/spanner và bbox. Trong MuseScore, track = staff × 4 + voice.
Chọn trực tiếp measure/range trong editor → tạo loop → đặt tempo/mode → lưu thành lesson. Khi score đổi revision, section được remap bằng EID + tick anchors.
Tách pitch, onset, duration, continuity và musical expression. Tổng điểm không che mất nguyên nhân sai; user luôn thấy cần sửa gì.
Mỗi phase phải kết thúc bằng bằng chứng import–edit–render–export, không chỉ một demo giao diện đẹp.
Build đúng commit đã pin, mở MSCZ demo, kiểm edit/playback/save; ghi bundle size, cold start, memory, browser matrix và source distribution procedure.
Thêm web preset, reader/writer, API nhận filename; mở 20 MXL đại diện, render, sửa một note, undo, export MXL và re-open. Đây là feasibility gate quan trọng nhất.
ImportHealthReport, secure intake, stable mapping, semantic/visual diff, snapshot/recovery và P0 command telemetry. Không silent data loss.
Hoàn thành L1/P0, keyboard + MIDI note input, selection/clipboard, properties, multi-voice, lyrics, deterministic undo/replay và incremental render budget.
L2/P1, PracticeIR theo revision, loop/tempo/playback, piano MIDI modes; vocal target view và pitch capture ở beta.
L3/L4 theo demand, linked parts, style/page layout, performance score lớn, autosave migration, fuzzing, accessibility và quy trình nâng upstream.
Notation editor phải được đánh giá bằng semantic fidelity và invariant, không chỉ bằng screenshot.
| Rủi ro | Tác động | Kiểm soát |
|---|---|---|
| GPL không phù hợp | Không thể phân phối kiến trúc WASM như dự kiến. | Gate pháp lý trước khi fork sâu; chuẩn bị clean-room/native options. |
| Qt/WASM bundle hoặc memory lớn | Cold start và mobile browser kém. | Đo ngay Phase 0; lazy load editor; practice viewer nhẹ là fallback. |
| MusicXML round-trip mất layout | Score đổi hình hoặc mất vendor extension. | Giữ original, diagnostics, support state và semantic diff. |
| Fork trôi khỏi upstream | Security fix và feature mới khó nhập. | Pin SHA, adapter nhỏ, patch queue rõ, rebase CI định kỳ. |
| Layout chậm khi edit | Caret/drag lag, score dài không dùng được. | Dirty-range, viewport virtualization, benchmark p95. |
| Advanced notation nổ scope | “Full editor” không bao giờ đạt. | L0–L4 support matrix; preserved/read-only thay vì silent loss. |
| Stable ID không bền | Practice result/comment trỏ sai note. | EID + source locator + sidecar mapping và migration tests. |
| MXL/XML không an toàn | Zip bomb, path traversal, resource exhaustion. | Sandbox, limits, no external entity, fuzzing. |
License: xác nhận mô hình phân phối, source delivery và asset manifest.
Feasibility: 20 MXL import, render, edit, undo và export/reopen trong browser.
Fidelity: P0 semantic round-trip 100%; unsupported luôn có diagnostic.
Interaction: command/undo deterministic; edit-to-paint p95 < 120 ms trên benchmark.
Publication scope: chỉ gọi “publication editor” khi P1 support contract và corpus đều pass.
Maintainability: nâng pinned MuseScore revision mà adapter/test suite vẫn kiểm soát được.
Snapshot theo MuseScore main@5f0f3d7. Cần pin commit cụ thể khi bắt đầu spike vì cấu hình upstream có thể thay đổi.
Xây score engine trước, editor workflow kế tiếp, practice intelligence ở phía trên. Dùng invariant và engraving của MuseScore nếu license và spike cho phép—không biến Musix thành một bản sao giao diện MuseScore.
Bước tiếp theo có giá trị nhất: timebox 10 ngày để chứng minh MXL → render → edit → undo → export → reopen trong MuseScore app-web.