Định hướng sản phẩm & kỹ thuật

MXL vào. Bản nhạc sống.

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.

Import / export MXL Full notation edit MuseScore WASM Piano MIDI Vocal pitch
Decision note DR-001 Trạng thái: đề xuất để spike Ưu tiên: editor core trước practice intelligence
01

Quyết định chính

Một canonical score, một engraving engine, nhiều trải nghiệm học nhạc.

Khuyến nghị Fork và pin đường build app-web chính thức của MuseScore, bật lại MusicXML/MXL, rồi mở rộng một web bridge nhỏ và ổn định.

Đâ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.

Primary

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.

Fallback

Native headless service

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.

Không ưu tiên

Clean-room web editor

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.

Gate 0 là license. MuseScore Studio dùng GPL-3.0-only. Phân phối bản WASM có liên kết mã MuseScore cần một chiến lược GPL rõ ràng và cung cấp source/build tương ứng. Font, soundfont và asset cần audit riêng. Đây là định hướng kỹ thuật, không phải tư vấn pháp lý.
02

Định hướng sản phẩm

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.

Mở & render

Nhập .mxl/.musicxml/.xml, render đúng engraving, playback và xuất lại.

Notation studio

Sửa trực tiếp score hoặc tạo mới: note input, structure, text, layout, parts và style.

Piano practice

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ộ.

Vocal practice

Hiển thị line/block theo cao độ; so đường hát của user với target bằng cents và timing.

5 · Collaboration Comment của giáo viên, version history, chia sẻ section, assignment.
4 · Practice MIDI/pitch capture, loop, scoring, heatmap, tempo ramp, mastery.
3 · Studio Selection, note input, palette, inspector, page/continuous view.
2 · Engraving Layout, font metrics, geometry, paint, hit-test, incremental invalidation.
1 · Score Core Import, DOM, commands, invariants, transaction, undo/redo, serialization.
Định nghĩa mới của source of truth: MXL là định dạng nhập/xuất. Sau import, MasterScore của engine là state chỉnh sửa chính; MSCZ là snapshot nội bộ, còn PracticeIR, playback timeline và mọi view là projection theo cùng một revision.
03

Phát hiện từ MuseScore GitHub

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.

01

Có build Qt/WASM chính thức

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.

02

MXL đang bị tắt trong app-web

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.

03

Web API hiện rất hẹp

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.

04

Editor core đã có command surface rộng

NotationInteraction, NotationNoteInput, InputState và transaction manager bao phủ note input, voice, tuplet, text, measure, transpose, selection, undo và hơn nữa.

05

Importer MXL đã trưởng thành

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.

06

Render có dirty range

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.

Kết luận: spike nên bắt đầu bằng việc build nguyên app-web upstream, không bắt đầu bằng việc “tách vài file C++”. Sau khi đo được hành vi thực, mới quyết định giữ Qt/QML UI đầy đủ hay tạo adapter headless. Đây là cách giảm rủi ro dependency ẩn.
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
04

Kiến trúc MuseScore-first

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ộ.

Domain hierarchy cần giữ nguyên ngữ nghĩa

EngravingProject MasterScore / Score Part Staff Measure Segment ChordRest Chord / Rest Note

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.

Canonical write model

MasterScore + revision + undo stack. Tất cả mutation đi qua MuseScore command/interaction layer trong một transaction.

Derived read models

Layout pages, playback event list, PracticeIR, thumbnail và search index có thể hủy rồi dựng lại từ canonical score.

Persistence

Autosave canonical MSCZ/snapshot + command journal. Giữ original MXL và ImportHealthReport để audit/round-trip.

05

Import MXL một lần, edit đầy đủ

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.

Bước 1 Secure intake MIME sniff, size limit, ZIP ratio, path validation, chặn XXE.
Bước 2 Container root Đọc META-INF/container.xml, tìm root MusicXML và asset hợp lệ.
Bước 3 Two-pass import Pass 1: structure/time/voice. Pass 2: note, spanner, lyrics, expression.
Bước 4 Normalize & report Dựng MasterScore, stable IDs, warnings và unsupported feature map.
Bước 5 Edit & export Canonical MSCZ; xuất MXL/PDF/audio và semantic re-import check.
Pass 1

Khung thời gian và cấu trúc

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.

Pass 2

Notation chi tiết

Chord/rest/note, tie/slur, tuplet, beam, lyric, dynamic, harmony, pedal, glissando, volta, tremolo và các direction/spanner khác.

Hợp đồng round-trip

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.
Hai chế độ import: Preserve source layout ưu tiên manual position/override từ file; Normalize with Musix style ưu tiên engraving nhất quán. Dù chọn chế độ nào, semantic diff phải kiểm pitch, spelling, duration, voice, tie, lyric, signature, repeat và part—not chỉ so text XML.
06

Full notation edit có nghĩa gì?

“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.
Editor kernel

P0 · Bắt buộc

  • Element/list/range selection và filter
  • Insert/delete note, rest, chord
  • Pitch, spelling, duration, dot, accidental
  • Voice 1–4, tie, stem, default beam
  • Add/delete/pickup measure
  • Clef, key và time signature
  • Copy/cut/paste, duplicate, transpose
  • Lyrics cơ bản, undo/redo, recovery
Common score

P1 · Hoàn chỉnh phổ biến

  • Tuplet, grace note, beam grouping
  • Slur, articulation, ornament, fingering
  • Dynamic, hairpin, tempo, rehearsal mark
  • Chord symbol, staff/system text
  • Repeat, volta, segno/coda/jump
  • Instrument, transposition, multi-staff
  • Ottava, pedal, trill và spanner
  • System/page/section break, basic style
Specialist

P2 · Publication nâng cao

  • Cross-staff và tremolo phức tạp
  • Percussion mapping, tablature
  • Nested/irregular tuplet
  • Microtonal accidental
  • Figured bass, ossia, custom symbol
  • Advanced frame/page layout
  • Part extraction và linked propagation
  • Manual engraving override chi tiết

Command, transaction và revision

// 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: []
}

Input mode nên giữ từ MuseScore

  • By note name
  • By duration
  • Repitch
  • Rhythm
  • Realtime automatic
  • Realtime manual
  • Timewise
  • MIDI note input

Invariant phải nằm trong core

  • Duration của voice/measure hợp lệ
  • Không có tie/spanner mồ côi
  • Written/concert pitch nhất quán
  • Linked part propagation đúng
  • Selection/cursor trỏ element còn sống
  • Command lỗi rollback toàn bộ
  • Undo trả về semantic state trước
  • Replay command deterministic

Selection là domain state

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.

Semantic command trước property set

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.

Một undo authority

UndoStack/TransactionManager trong core là authority. Web chỉ gửi baseRevision + commandId; không tạo thêm JS undo stack cho score.

07

Render là một phần của editor

Renderer không chỉ trả ảnh. Nó phải trả đúng geometry cho caret, selection, hit-test, drag và practice overlay.

Mutation Edit transaction Command chạy atomic trên MasterScore.
Delta ScoreChanges Changed type/property/style + dirty tick/staff range.
Layout layoutRange Tính lại measure/system/page và shape bị ảnh hưởng.
Paint paintScore Qt/muse::draw dùng SMuFL font và layout geometry.
Interact Canvas + overlay Selection, cursor, MIDI/pitch highlight dùng cùng element ID.

Renderer phải xuất

  • Paint output/display list hoặc Qt canvas surface
  • Bounding box + hit region theo element ID
  • Caret và vị trí note-input
  • Path/handle của slur, hairpin và spanner
  • Measure, system, page và viewport bounds
  • Mapping tick/staff ↔ screen coordinates

Performance strategy

  • Dirty-range re-layout thay vì full score
  • Virtualize page/system ngoài viewport
  • Coalesce drag preview, layout khi commit
  • Giữ engine off main UI loop nếu kiến trúc Qt cho phép
  • Cache glyph/font metrics và page tiles
  • Đo score 50–200 trang ngay trong spike
Không render thành ảnh nguyên trang rồi đoán vị trí nốt ở JavaScript. Geometry và hình phải sinh từ cùng một layout engine. Nếu web overlay cần hit map, bridge phải xuất dữ liệu đó hoặc chuyển interaction vào chính Qt scene.
Hit-test đã có trong core: NotationInteraction query BSP tree của page, kiểm Shape contains/intersects rồi sắp theo priority, z-order và track. Device coordinates phải được đổi sang logical coordinates trước khi query. Khi một edit làm page reflow, invalidation có thể lan từ dirty system đến các page sau—không nên hứa rằng mỗi command chỉ render lại đúng một measure.
08

Web bridge tối thiểu

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.

API đề xuất cho spike

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)

Thay đổi upstream tối thiểu

  1. Bổ sung Musix web preset bật MUE_BUILD_IMPEXP_MUSICXML_MODULE.
  2. Thay load hard-code current.mscz bằng loadNamed có extension/format rõ.
  3. Đăng ký reader/writer cho xml/musicxml/mxl trong WASM.
  4. Thêm callbacks nhỏ cho revision, selection, import diagnostics và save bytes.
  5. Nối Web MIDI từ JavaScript vào note-input/practice vì module MIDI native đang tắt.
  6. Không patch sâu UI cho practice; đưa overlay/workflow qua adapter trước.
  7. Pin commit SHA, giữ patch queue nhỏ và chạy rebase test định kỳ.

Ranh giới ownership

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.
09

Practice nằm trên cùng score

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.

Piano MIDI

Ba chế độ tập

  • Learn notes: playback chờ user đánh đúng pitch/chord.
  • Rhythm: tập onset/duration với pitch đã hỗ trợ hoặc cố định.
  • Perform: chạy theo tempo, chấm pitch và timing riêng.
  • Loop theo measure/selection, tempo ramp và count-in.
  • Highlight note bằng EID, hỗ trợ tay trái/phải và voice filter.
Vocal pitch

Nhiều view, một target

  • Staff view truyền thống với lyric/caret.
  • Pitch line: target curve so với user curve theo thời gian.
  • Block ladder: mỗi note là block, user hiện trên/dưới theo cents.
  • Confidence gate để bỏ unvoiced/noise; không chấm im lặng như sai pitch.
  • Live feedback nhẹ; alignment/scoring chính xác hơn sau take.

PracticeIR

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.

Section workflow

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.

Feedback model

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ì.

10

Roadmap theo decision gate

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.

Phase 0 · 3–5 ngày

Reproduce upstream WASM

Build đúng commit đã pin, mở MSCZ demo, kiểm edit/playback/save; ghi bundle size, cold start, memory, browser matrix và source distribution procedure.

Phase 1 · 5–8 ngày

Bật MusicXML/MXL và loadNamed

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.

Phase 2 · 2–4 tuần

Fidelity foundation

ImportHealthReport, secure intake, stable mapping, semantic/visual diff, snapshot/recovery và P0 command telemetry. Không silent data loss.

Phase 3 · 4–8 tuần

Composer kernel

Hoàn thành L1/P0, keyboard + MIDI note input, selection/clipboard, properties, multi-voice, lyrics, deterministic undo/replay và incremental render budget.

Phase 4 · 6–12 tuần

Structural score + practice bridge

L2/P1, PracticeIR theo revision, loop/tempo/playback, piano MIDI modes; vocal target view và pitch capture ở beta.

Phase 5 · liên tục

Publication & hardening

L3/L4 theo demand, linked parts, style/page layout, performance score lớn, autosave migration, fuzzing, accessibility và quy trình nâng upstream.

10 ngày đầu nên trả lời đúng 8 câu hỏi

  1. Build app-web upstream có tái lập được không?
  2. UI edit nào thực sự hoạt động trong browser?
  3. Bật MusicXML làm bundle/startup tăng bao nhiêu?
  4. MXL importer có cần filesystem/thread API bị thiếu không?
  1. Load → edit → undo → export/reopen có pass không?
  2. Geometry/selection events có bridge sạch được không?
  3. Score 200 trang dùng memory/latency bao nhiêu?
  4. GPL/source delivery có phù hợp mô hình sản phẩm không?
11

Chất lượng, test và rủi ro

Notation editor phải được đánh giá bằng semantic fidelity và invariant, không chỉ bằng screenshot.

Test corpus 150–250 score

  • MusicXML official examples + fixture có license phù hợp
  • Piano grand staff, vocal/lyrics, ensemble/orchestral
  • Pickup, tuplet, repeat, volta, jump, tempo changes
  • Cross-staff, percussion, tablature, microtonal
  • Text/frame/page break và score 50–200 trang
  • File lỗi, ZIP bomb, XML độc hại và fuzz-generated

Mỗi fixture phải chạy

  1. Import không crash + diagnostics đủ.
  2. Visual regression theo page/system.
  3. Export → reimport → semantic comparison.
  4. Playback event/timeline comparison.
  5. Random commands → undo all → state ban đầu.
  6. Differential test với MuseScore commit đã pin.
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.

Quality gates

Gate 0

License: xác nhận mô hình phân phối, source delivery và asset manifest.

Gate 1

Feasibility: 20 MXL import, render, edit, undo và export/reopen trong browser.

Gate 2

Fidelity: P0 semantic round-trip 100%; unsupported luôn có diagnostic.

Gate 3

Interaction: command/undo deterministic; edit-to-paint p95 < 120 ms trên benchmark.

Gate 4

Publication scope: chỉ gọi “publication editor” khi P1 support contract và corpus đều pass.

Gate 5

Maintainability: nâng pinned MuseScore revision mà adapter/test suite vẫn kiểm soát được.

Performance budget ban đầu

  • Edit-to-paint p95 dưới 120 ms với score thường
  • First page dưới 3 giây cho benchmark score lớn
  • Scroll gần 60 fps trong viewport
  • Không khóa web UI quá 50 ms ngoài startup
  • Không silent data loss trong P0 corpus

Chưa làm trước

  • Realtime collaboration/CRDT
  • Mobile full editor và PDF/image OMR
  • Plugin marketplace hoặc sound library lớn
  • Acoustic piano recognition
  • Pixel parity với mọi MuseScore version
  • Vocal AI nâng cao trước khi timeline ổn định
12

Nguồn kỹ thuật đã đối chiếu

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.