Skip to content

Instantly share code, notes, and snippets.

@jdhitsolutions
Last active May 17, 2025 17:51
Show Gist options
  • Select an option

  • Save jdhitsolutions/ca3be345e677337ea2e4d585bc1bd609 to your computer and use it in GitHub Desktop.

Select an option

Save jdhitsolutions/ca3be345e677337ea2e4d585bc1bd609 to your computer and use it in GitHub Desktop.
A PowerShell function to use the MuseScore3 command-line tools to export a score file and its parts. You will need to dot-source this ps1 file and then run the function.
#requires -version 5.1
Function Export-MuseScore {
<#
.Synopsis
Export a Musescore3 score
.Description
The command line options for Musescore 3.6 appear to not work properly or as expected
on a Windows platform. This PowerShell function will take a mscz score file and export
it to MXL, PDF and an MP3. Optionally, you can also export parts to individual PDF files.
This will also create .mscx files for each part as well as the score.
C:\Program Files (x86)\MuseScore 3\bin should be in your %PATH% or you can try setting an
alias:
Set-Alias -name musescore3 -Value 'C:\Program Files (x86)\MuseScore 3\bin\MuseScore3.exe'
You should run this command in the folder with the Musescore file. Any existing files
will be overwritten.
.Parameter Score
Specify the .mscz file.
.Parameter Parts
Export parts to PDF and .mscx files.
.Example
PS C:\Scores\StringTrio> Export-Musescore StringTrio.mscz -parts
PS C:\Scores\StringTrio> Get-ChildItem
Directory: C:\Scores\StringTrio
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 7/8/2021 10:20 AM 374993 stringtrio-Viola.mscx
-a---- 7/8/2021 10:20 AM 109402 stringtrio-Viola.pdf
-a---- 7/8/2021 10:20 AM 389236 stringtrio-Violin.mscx
-a---- 7/8/2021 10:20 AM 116168 stringtrio-Violin.pdf
-a---- 7/8/2021 10:20 AM 323591 stringtrio-Violoncello.mscx
-a---- 7/8/2021 10:20 AM 102907 stringtrio-Violoncello.pdf
-a---- 7/8/2021 10:20 AM 4581667 stringtrio.mp3
-a---- 7/8/2021 10:19 AM 2094812 stringtrio.mscx
-a---- 7/3/2021 12:57 PM 71373 stringtrio.mscz
-a---- 7/8/2021 10:19 AM 191923 stringtrio.pdf
-a---- 7/8/2021 10:19 AM 1041938 stringtrio.mxl
Export the score and parts. If you want additional formats for parts, like an MP3 you can
manually run a command like: musescore3 -o viola.mp3 .\stringtrio-Viola.mscx
.Notes
Learn more about PowerShell: http://jdhitsolutions.com/blog/essential-powershell-resources/
.Link
https://musescore.org/en/handbook/3/command-line-options
#>
[cmdletbinding(SupportsShouldProcess)]
[OutputType("System.IO.File")]
[alias("ems")]
Param(
[Parameter(Position = 0,Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = "The mscz Musescore file to process.")]
[Alias("pspath", "fullname")]
[ValidatePattern('.*\.mscz$')]
[ValidateScript({ Test-Path $_ })]
[string]$Score,
[parameter(HelpMessage = "Export parts to PDF and mscx files")]
[switch]$Parts
)
begin {
Write-Verbose "Starting $($MyInvocation.MyCommand)"
#Write-progress parameters
$prog = @{
Activity = $MyInvocation.MyCommand
Status = "Processing"
CurrentOperation = "Initializing"
PercentComplete = 0
}
} #begin
Process {
$file = Get-Item $score
Write-Verbose "Processing $($file.name)"
$prog.Status = "Processing $($file.name)"
Write-Progress @prog
$dir = Split-Path -Path $file.FullName -Parent
$pdf = Join-Path $dir -ChildPath "$($file.basename).pdf"
$mp3 = Join-Path $dir -ChildPath "$($file.basename).mp3"
$xml = Join-Path $dir -ChildPath "$($file.basename).mxl"
$mscx = Join-Path $dir -ChildPath "$($file.basename).mscx"
$prog.PercentComplete = 25
$prog.CurrentOperation = "Exporting to $($file.basename).pdf"
Write-Progress @prog
if ($pscmdlet.ShouldProcess($pdf, "Export PDF")) {
#Write-Verbose "Saving $pdf" -ForegroundColor yellow
musescore3.exe -o $pdf $file.fullname
Start-Sleep -Seconds 10
}
$prog.PercentComplete = 50
$prog.CurrentOperation = "Exporting to $($file.basename).xml"
Write-Progress @prog
if ($pscmdlet.ShouldProcess($xml, "Export XML")) {
#Write-Verbose "Saving $xml" -ForegroundColor Yellow
MuseScore3.exe -o $xml $file.fullname
Start-Sleep -Seconds 5
}
$prog.PercentComplete = 75
$prog.CurrentOperation = "Exporting to $($file.basename).mp3"
Write-Progress @prog
if ($pscmdlet.ShouldProcess($mp3, "Export MP3")) {
MuseScore3.exe -o $mp3 $file.fullname
Start-Sleep -Seconds 10
}
if ($Parts -AND $Pscmdlet.ShouldProcess($mscx, "Create MSCX file")) {
Write-Verbose "Creating $($file.basename).mscx"
$prog.PercentComplete = 80
$prog.CurrentOperation = "Exporting to $($file.basename).mscx"
Write-Progress @prog
MuseScore3.exe -o $mscx $file.fullname
#give the command time to complete
Start-Sleep -Seconds 20
}
if ($Parts -AND (Test-Path $mscx)) {
$prog.CurrentOperation = "Exporting parts from $($file.basename).mscx"
$prog.PercentComplete = 85
Write-Progress @prog
#define parameters for a child progress bardir
$childProg = @{
ParentID = 0
ID = 1
Activity = "Generating parts"
Status = "Getting content"
CurrentOperation = $mscx
PercentComplete = 0
}
Write-Progress @childProg
[xml]$score = Get-Content -Path $mscx
$scoreparts = $score.museScore.Score.score
Write-Verbose "Found $($scoreparts.count) parts"
$childProg.CurrentOperation = "Found $($scoreparts.count) parts"
$childProg.PercentComplete = 1
Write-Progress @childProg
$i = 0
foreach ($part in $scoreparts) {
Write-Verbose "Processing part for $($part.name)"
$i++
$childProg.PercentComplete = ($i/$scoreparts.count)*100
$partbase = "{0}-{1}" -f $file.basename, $part.name.replace("/","_")
$partname = "$partbase.mscx"
$partFile = Join-Path $dir -ChildPath $partname
$partPDF = Join-Path $dir -ChildPath "$partbase.pdf"
$childProg.status = $part.name
$childProg.CurrentOperation = "Exporting to $partbase.pdf"
Write-Progress @childProg
$r = $score.museScore.SelectSingleNode("//Score")
[void]$score.museScore.ReplaceChild($part, $r)
if ($pscmdlet.shouldProcess($partFile, "Save part")) {
$score.Save($partFile)
if ($pscmdlet.ShouldProcess($partFile, "ExportPDF")) {
MuseScore3.exe -o $partPDF $partFile
start-Sleep -Seconds 5
} #should export part pdf
} #should save part
} #foreach part
}
elseif ( $Parts -AND (-not $WhatIfPreference)) {
Write-Warning "Failed to create $mscx"
}
} #process
end {
$prog.completed
Write-Progress @prog
Write-Verbose "Ending $($MyInvocation.MyCommand)"
} #end
} #close function
#you can include this code in the file above or run it separaately to define an autocompleter for the Score parameter.
Register-ArgumentCompleter -CommandName Export-MuseScore -ParameterName Score -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
#PowerShell code to populate $wordtoComplete
(Get-Childitem *.mscz).Name |
ForEach-Object {
# completion text,listitem text,result type,Tooltip
[System.Management.Automation.CompletionResult]::new("'$_'", "'$_'", 'ParameterValue', "'$_'")
}
}
@milnak

milnak commented May 11, 2025

Copy link
Copy Markdown

FYI, this is still broken on Windows MuseScore4 and I have a script for export: https://gist.github.com/milnak/812c7b2ac2ac584fb3aa5f438003514a

@jdhitsolutions

Copy link
Copy Markdown
Author

I didn't have any problems using your function.

image

Are you getting any errors? Are they PowerShell errors or Musescore4.exe errors?

@milnak

milnak commented May 11, 2025

Copy link
Copy Markdown

I was just saying that I created a musescore4 solution that works as your script seems to be focused on musescore3

@jdhitsolutions

Copy link
Copy Markdown
Author

I understand now.

@jdhitsolutions

Copy link
Copy Markdown
Author

I also took a stab at enhancing your code so that it can take pipeline input.

#requires -version 7.5

function Export-MuseScoreToPdf {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-Path $_ -PathType Leaf }, ErrorMessage = 'The file {0} does not exist.' )]
        [ValidatePattern('\.mscz$', ErrorMessage = 'The file {0} does not appear to be a MuseScore file.')]
        [string]$File,
        [switch]$Passthru
    )
    Begin {
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN  ] Starting $($MyInvocation.MyCommand)"
        [string]$MuseScorePath = (Join-Path "$env:ProgramFiles" 'MuseScore 4\bin\MuseScore4.exe')
    } #begin
    Process {
        Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $file"

        $path = Resolve-Path -LiteralPath $File
        #get parent folder. Create all PDFs in the same folder as the mscz file
        $folder = Split-Path -Path $path -Parent

        Write-Verbose ("[$((Get-Date).TimeOfDay) PROCESS] Extracting score and parts from {0}" -f (Split-Path -Path $path -Leaf))

        $tempfile = Join-Path -path $env:temp -ChildPath 'musescore-score-parts.json'

        Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Extracting JSON"

        #TODO: convert to splatting
        Start-Process -WhatIf:$false -Wait -NoNewWindow `
            -WorkingDirectory (Get-Location).Path `
            -FilePath """$MuseScorePath""" `
            -ArgumentList '--score-parts-pdf', """$path""", '--export-to', """$tempfile"""

        $json = Get-Content $tempfile | ConvertFrom-Json
        $name = Split-Path -LeafBase $json.score
        $fileName = Join-Path -path $folder -ChildPath ("{0}-Score.pdf" -f $name) # $json.scoreFullPostfix
        Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Extracting score $name"

        Set-Content -LiteralPath $fileName -AsByteStream -Value ([Convert]::FromBase64String($json.scoreBin)) -WhatIf:$False

        for ($i = 0; $i -lt $json.parts.Count; $i++) {
            $partName = $json.parts[$i]
            Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Extracting part $partName to PDF"
            $filePart = "{0}-{1}.pdf" -f (Split-Path -LeafBase $name), $partName
            $fileName = Join-Path -path $folder -ChildPath $filePart
            if ($PSCmdlet.ShouldProcess("Part $fileName", "Convert to PDF [$partName]")) {
                Set-Content -LiteralPath $fileName -AsByteStream -Value ([Convert]::FromBase64String($json.partsBin[$i]))
                If ($Passthru) {
                    Get-Item $fileName
                }
            }
        } #for

        Remove-Item -LiteralPath $tempfile -WhatIf:$False
    } #process
    End {
        Write-Verbose "[$((Get-Date).TimeOfDay) END    ] Ending $($MyInvocation.MyCommand)"
    } #end
}

image

image

@milnak

milnak commented May 12, 2025

Copy link
Copy Markdown

Nice, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment