Skip to content

Fix Sibelius MusicXML import#595

Merged
AlexDumo merged 3 commits intoOpenMarch:mainfrom
dhruvachakravarthi:bugfix/579-import-zero-tempo
Aug 7, 2025
Merged

Fix Sibelius MusicXML import#595
AlexDumo merged 3 commits intoOpenMarch:mainfrom
dhruvachakravarthi:bugfix/579-import-zero-tempo

Conversation

@dhruvachakravarthi
Copy link
Collaborator

@dhruvachakravarthi dhruvachakravarthi commented Aug 5, 2025

Resolves #579

Summary by CodeRabbit

  • New Features
    • Introduced a utility to extract MusicXML data from compressed (.mxl) files, allowing users to upload and parse .mxl files directly.
  • Improvements
    • Enhanced tempo parsing for MusicXML files, providing more reliable tempo detection and fallback handling for uncommon time signatures.
    • Adjusted default tempo initialization for improved accuracy.
    • Streamlined module exports for better code organization without affecting functionality.
  • Bug Fixes
    • Improved handling of unsupported time signatures to prevent errors during parsing.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 5, 2025

Walkthrough

The changes reorganize the exports in the MusicXML parser package, moving type definitions and utility functions to a new utils.ts file. The main parser logic now imports these types and utilities from the new module. Tempo parsing is improved to avoid zero-tempo errors by adding fallback tempo extraction. Legacy .mxl file extraction code is removed from the parser and handled exclusively in the utility module.

Changes

Cohort / File(s) Change Summary
Export Reorganization
packages/musicxml-parser/index.ts
Export sources for Beat, Measure, and extractXmlFromMxlFile shifted from parser to utils.
Parser Logic and Cleanup
packages/musicxml-parser/src/parser.ts
Removes local type definitions and utility functions, imports them from utils.ts. Improves tempo parsing logic to avoid zero tempo, removes .mxl extraction and legacy code.
New Utilities Module
packages/musicxml-parser/src/utils.ts
Introduces Beat and Measure interfaces, utility functions for tempo calculation and .mxl extraction, and includes deprecated legacy code as comments.
Test Import Adjustments
packages/musicxml-parser/src/__test__/*
Updates import paths for parseMusicXml (from parser) and other utilities (extractXmlFromMxlFile, Beat, Measure, secondsPerQuarterNote) from parser to utils. Adds beatsEqual import in some tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Parser
    participant Utils

    Client->>Parser: parseMusicXml(xmlString)
    Parser->>Utils: secondsPerQuarterNote(tempo)
    Parser->>Utils: Beat, Measure types
    Client->>Utils: extractXmlFromMxlFile(fileBuffer)
    Utils-->>Client: XML string
    Parser-->>Client: Parsed result
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Assessment against linked issues

Objective Addressed Explanation
Prevent creation of zero-tempo groups and avoid zero BPM in MusicXML parsing (#579)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Poem

A rabbit hopped through code anew,
Sorting types and utils too!
No more zero-tempo fright,
Now the music plays just right.
With beats and measures, zipped and neat—
This parser’s fix is quite a feat!
🐇🎶

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a2f47a and bf7cbe6.

📒 Files selected for processing (2)
  • packages/musicxml-parser/src/parser.ts (5 hunks)
  • packages/musicxml-parser/src/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/musicxml-parser/src/utils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: desktop-e2e (main, main)
  • GitHub Check: desktop-e2e (bugfix/579-import-zero-tempo, pull-request)
  • GitHub Check: build-and-release (ubuntu-latest)
  • GitHub Check: build
  • GitHub Check: build-and-release (macos-13)
  • GitHub Check: build-and-release (macos-latest)
🔇 Additional comments (4)
packages/musicxml-parser/src/parser.ts (4)

1-1: LGTM! Good refactoring for modularity.

Moving types and utilities to a separate module improves code organization and follows separation of concerns principles.


11-11: Excellent fix for the zero-tempo crash issue.

Changing the default tempo from 0 to 120 BPM directly addresses the core problem described in issue #579, preventing crashes caused by zero-tempo values that result in non-finite currentTime values on HTMLMediaElement.


32-32: Good addition of 7/8 time signature support.

The mapping [7, 1] correctly represents 7 beats per measure with each beat being an eighth note (1 quarter note equivalent), expanding the parser's time signature support.


65-72: Excellent Sibelius-specific tempo parsing fix.

This fallback logic perfectly addresses the issue described in #579. When standard <sound tempo="..."> attributes are not found, the parser now extracts tempo from <per-minute> tags, which is how Sibelius exports tempo information. This prevents zero-tempo crashes while maintaining compatibility with different MusicXML export formats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/musicxml-parser/src/utils.ts (1)

75-103: Remove commented legacy code.

The commented-out parseMxl function should be removed entirely rather than kept as commented code, as it adds clutter and the comment indicates it's deprecated.

Apply this diff to remove the legacy code:

-/**
- * Parses score xml as a string from an MXL file
- * @param filePath path to .mxl file on disk
- * @returns parsed XML or undefined if an error occurred
- *
- * Legacy function to parse MXL files.
- */
-/*
-export function parseMxl(filePath: string): string | undefined {
-    try {
-        var zip = new AdmZip(filePath);
-        var zipEntries = zip.getEntries();
-
-        // Find the root container, this will tell us which zip entry contains the actual score data we care about.
-        const rootContainer = zipEntries.find(
-            (entry: any) => entry.entryName === "META-INF/container.xml",
-        );
-
-        // Parse the root container data to a string
-        const rootContainerData = rootContainer.getData().toString("utf8");
-
-        // Pull out the root file path using a regex (I gave up on parsing this "correctly" using xml)
-        const regex = /rootfile full-path="([^"]*)"/;
-        const match = rootContainerData.match(regex);
-        let scorePath = match ? match[1] : undefined;
-
-        // Find the score container and create a string from its xml data
-        let scoreContainer = zipEntries.find(
-            (zipEntry: any) => zipEntry.entryName === scorePath,
-        );
-        return scoreContainer.getData().toString("utf8");
-    } catch (error) {
-        console.error("Error parsing MXL:", error);
-        return undefined;
-    }
-}*/
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51807e9 and ee28351.

📒 Files selected for processing (3)
  • packages/musicxml-parser/index.ts (1 hunks)
  • packages/musicxml-parser/src/parser.ts (5 hunks)
  • packages/musicxml-parser/src/utils.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build-and-release (macos-13)
  • GitHub Check: build-and-release (ubuntu-latest)
  • GitHub Check: build
  • GitHub Check: build-and-release (macos-latest)
🔇 Additional comments (7)
packages/musicxml-parser/index.ts (1)

1-2: LGTM! Clean export reorganization.

The refactoring properly separates utility types and functions into a dedicated utils module while maintaining clear exports. This improves code organization and follows good separation of concerns.

packages/musicxml-parser/src/utils.ts (2)

9-31: Well-designed interfaces with clear documentation.

The Beat and Measure interfaces are properly typed and include comprehensive JSDoc comments that clarify the purpose of each property. The distinction between musical notes and textual notes is particularly well-documented.


46-66: Robust file extraction with proper error handling.

The function correctly handles .mxl file extraction by filtering out metadata files and container files, then returns the first valid XML content found. The error handling for missing XML files is appropriate.

packages/musicxml-parser/src/parser.ts (4)

1-1: LGTM! Proper import of utilities.

The import correctly references the newly created utils module for the relocated types and functions.


11-11: Excellent fix for the zero tempo issue!

Changing the default tempo from 0 to 120 BPM directly addresses the core problem described in issue #579. This prevents zero-tempo conditions that cause application crashes.


65-72: Good fallback tempo parsing strategy.

The enhanced tempo parsing logic provides a robust fallback by checking <per-minute> tags when <sound tempo="..."> is not available. This improves compatibility with different MusicXML formats from Sibelius.


32-32: Minor cleanup: removed inline comment.

The removal of the inline comment on the "7/8" time signature entry is a minor cleanup that improves code consistency.

const [bigBeatCount, quarterNotesPerBigBeat] = bigBeats[
timeSignature
] ?? [0, 0];
] ?? [1, 1];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the time signature fallback change.

The fallback for unknown time signatures changed from [0, 0] to [1, 1]. While this prevents zero beat counts, ensure this doesn't introduce incorrect behavior for edge cases.

Run the following script to verify how unknown time signatures are handled:


🏁 Script executed:

#!/bin/bash
# Description: Search for time signature handling and validation in the codebase
# Expected: Find if there are any tests or other validations for unknown time signatures

echo "=== Searching for time signature tests ==="
rg -A 5 -B 5 "time.?signature" --type js --type ts

echo "=== Searching for bigBeats usage ==="
rg -A 3 -B 3 "bigBeats\[" --type js --type ts

echo "=== Searching for test files related to musicxml ==="
fd -e test.ts -e spec.ts -x rg -l "musicxml\|MusicXML" {} \;

Length of output: 32560


Double-check fallback behavior for unknown time signatures
The default for bigBeats[timeSignature] was changed from [0, 0] (which threw an error) to [1, 1], silently allowing one quarter-note beat for any unrecognized signature. I didn’t find any existing tests or handling for malformed/missing time signatures in the parser, so please:

  • Confirm that defaulting to a single quarter-note beat is the intended fallback.
  • Add tests covering cases with no <time> element or invalid <beats>/<beat-type> values to ensure this behavior is correct.
  • (Optional) Emit a warning or document this fallback so consumers aren’t surprised by silent defaults.

Locations needing attention:

  • packages/musicxml-parser/src/parser.ts, around line 97:
    const [bigBeatCount, quarterNotesPerBigBeat] = bigBeats[timeSignature] ?? [1, 1];
    if (bigBeatCount === 0) {  }
🤖 Prompt for AI Agents
In packages/musicxml-parser/src/parser.ts at line 97, the fallback for unknown
time signatures was changed from [0, 0] to [1, 1], which silently defaults to a
single quarter-note beat. Confirm if this fallback is intended; if so, add tests
for cases with missing or invalid <time> elements and invalid
<beats>/<beat-type> values to verify this behavior. Additionally, consider
adding a warning log or documentation to inform users about this silent default
to avoid surprises.

@AlexDumo
Copy link
Collaborator

AlexDumo commented Aug 7, 2025

Awesome, thanks!

@AlexDumo AlexDumo merged commit f2b8773 into OpenMarch:main Aug 7, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: MusicXML creates zero-tempo

2 participants