OM Score Transformer — User Guide

Native-Praat symbolic score transformation: read MusicXML, apply a fixed chain of pitch/chord/order/time operations, then render the transformed notes with additive synthesis.

Author: Shai CohenVersion: 0.4 (2026)Input: MusicXML file or Praat Strings objectOutput: mono Sound
Contents:

What this does

OM Score Transformer works on symbolic notes rather than on an existing audio waveform. It parses an uncompressed MusicXML score into note records containing onset, end time, MIDI pitch and amplitude; applies a fixed series of transformations; and synthesizes the result directly in Praat with a harmonic additive bank.

MusicXML / Strings ↓ 1. FILTER duration + MIDI register ↓ 2. PITCH transpose → inversion ↓ 3. CHORD arpeggiate / thin / voice-lead ↓ 4. ORDER retrograde / rotate onset groups ↓ 5. TIME whole-score scale → note-length scale ↓ 6. SYNTH mono additive harmonic-bank render

The order is fixed. Earlier stages change the material seen by later stages: for example, the chord stage operates on pitches after transposition and inversion.

No external runtime is required. Version 0.4 uses a native Praat MusicXML parser. It does not require Python, music21 or pip.

Score input & MusicXML

Two score sources

SourceBehavior
File on diskReads an uncompressed .musicxml or .xml file. If the field is blank, the script opens a file chooser. Leading/trailing whitespace and surrounding double quotes are removed from a typed path.
Selected Strings objectUses exactly one Praat Strings object selected before the script starts. The object is written to a temporary MusicXML file and sent through the same parser as the file-input path. This supports direct handoff from a Strings-based transcription workflow.
Compressed .mxl is not supported. An .mxl file is a ZIP archive; this script does not unzip it. Export uncompressed MusicXML instead.

Praat 7.0 and later may request permission to write temporary files. Temporary parser files are removed by the script when processing completes normally.

Parser behavior

The parser handles score parts, divisions, chords, rests, grace-note omission, voices using backup/forward, pitch alteration, and ties. Tied note segments with matching part, voice and MIDI pitch are joined into a longer note. Grace notes are ignored.

When a dynamics attribute is present on a note tag, its value is divided by 100 and limited to a maximum of 1.0. Otherwise the note amplitude defaults to 0.7. These amplitudes are retained through the symbolic transformations and drive the additive render.

Tempo

Tempo_bpm = 0 means “read tempo from the score.” Version 0.4 resolves a single global tempo in this priority order:

1. Tempo_bpm from the form, if > 0 2. First MusicXML <sound tempo="..."> value 3. First MusicXML metronome mark, converted from its beat-unit 4. 120 BPM when no tempo is found

Metronome values respect beat-unit and any beat-unit-dot elements; for example, an eighth-note or dotted-quarter metronome mark is converted to quarter-note BPM before note times are converted to seconds.

Single-tempo design: the renderer does not build a tempo map. If later tempo values differ from the first chosen score tempo, it reports a warning and uses the first chosen value throughout. A form tempo override likewise establishes one tempo for the entire score.

Fixed transformation chain

The transformations are not independent plug-ins: they run in the following exact sequence.

StageOperationImportant behavior
1FilterRejects notes shorter than Min_note_duration_s or outside the inclusive MIDI range Lowest_midi … Highest_midi.
2PitchApplies transposition first, then optional inversion. Out-of-range results are octave-folded back into MIDI 0–127.
3ChordBuilds simultaneity groups using a 12 ms onset tolerance, then optionally arpeggiates, thins or re-octaves chord members.
4OrderOptionally performs a time-domain retrograde or rotates onset groups with their inter-onset intervals.
5TimeScales all onsets and durations, then separately scales note lengths while keeping transformed onsets fixed.
6SynthesisRenders the final note table into a mono Sound using a harmonic bank and an amplitude envelope.

Parameters

ParameterDefaultMeaning
Score sourceFile on diskChoose a disk file or the Strings object selected before running.
Score fileblankPath to uncompressed MusicXML/XML. Blank opens a chooser in interactive use.
Tempo bpm00 = resolve tempo from MusicXML; positive value overrides score tempo globally.
Min note duration s0Remove notes whose parsed duration is shorter than this value.
Lowest MIDI0Lowest accepted source pitch in the filter stage.
Highest MIDI127Highest accepted source pitch in the filter stage. Lowest must not exceed Highest.
Transpose semitones0Added to every surviving MIDI pitch before inversion.
Invert modenoneNone, inversion about a fixed MIDI axis, or inversion about the mean pitch after transposition.
Invert axis MIDI60Axis for fixed-axis inversion. Ignored by mean-pitch inversion.
Chord modenoneNone, three arpeggiation orders, three thinning modes, or voice-leading.
Arpeggio stride ms70Time offset between successive ranks in an arpeggiated simultaneity group.
Keep per chord3Maximum number retained by the three thinning modes.
Order modenoneNone, retrograde, or rotate onset groups.
Rotate steps1Number of group positions used by rotation. Negative values wrap; multiples of the group count are identity.
Time scale1.0Multiplies transformed note onsets and ends together.
Note length scale1.0Multiplies durations only, leaving onsets fixed; resulting duration is never shorter than 10 ms.
Edit synthesis settingsnoOpens the secondary synthesis dialog.
Draw visualizationyesDraws score, audio and summary panels in the Picture window.
Play resultyesPlays the rendered Sound after completion.

Pitch transformation

Transposition occurs before inversion.

p₁ = MIDI + Transpose_semitones fixed-axis inversion: p₂ = 2 × Invert_axis_midi − p₁ mean-pitch inversion: axis = round(mean(p₁ over all surviving notes)) p₂ = 2 × axis − p₁

After the complete pitch operation, values below 0 or above 127 are moved by whole octaves until they lie in the legal MIDI range. This preserves pitch class but changes octave. The report states how many notes required this octave folding.

Chord operations

Chord operations first sort notes by onset and descending pitch. Notes whose onsets fall within 12 ms of the first onset of a group are treated as one simultaneity group.

Arpeggiate up / down / up-down

Each chord member receives a multiple of Arpeggio_stride_ms. Because groups are internally sorted high-to-low, the rank is reversed for “up” so lower pitches enter first. “Down” uses the sorted order directly. “Up-down” alternates from the low and high ends.

An arpeggiated onset is limited to at most 20 ms before the chord's original latest end. If necessary, note duration is adjusted to maintain at least 20 ms, but the operation does not push a note beyond that group end. Thus arpeggiation does not lengthen the score merely by spreading a chord.

Thin

Groups already at or below the requested retained count are unchanged.

Voice-lead

The first group establishes a reference centre. In each later group, every member is tested at octave displacements from −3 to +3 octaves and moved to the legal MIDI octave nearest the previous group's centre. Pitch class is preserved. The centre of the re-octaved group becomes the reference for the next group.

This is an octave-placement heuristic, not a full voice-assignment or contrapuntal optimization algorithm.

Order & time operations

Retrograde

Retrograde mirrors every note interval around the transformed score end:

[start, end] → [scoreEnd − end, scoreEnd − start]

Each note keeps its duration, and the symbolic score length is preserved. This is a temporal mirror of the score, not merely a reversal of the internal note list.

Rotate groups

Rotation rebuilds the onset-group sequence starting at another group while carrying each group's inter-onset interval with it. The resulting IOI sequence is therefore a rotation of the original IOI sequence. Negative steps wrap modulo the number of groups.

Rotation does not guarantee preservation of total duration. Notes carry their original durations with the moved groups. A long note moved later may extend beyond the source end.

Time scale vs. note length scale

Time scale multiplies both starts and ends, so it performs whole-score augmentation/diminution. Note length scale is applied afterwards and changes duration only. Values above 1 can therefore create new overlaps; values below 1 produce shorter articulations, with a 10 ms minimum duration.

After these transformations, the earliest resulting onset is shifted to time 0. The audio duration then includes the transformed score extent plus the synthesis release time and an additional 50 ms margin.

Additive synthesis

The result is a mono Sound. Each transformed MIDI note is converted to frequency with:

f = 440 × 2^((MIDI − 69) / 12)

Each note is synthesized as a harmonic series. Harmonic k has amplitude:

aₖ = 1 / k^SpectralTilt

The bank uses deterministic Schroeder phases:

φₖ = −π k(k−1) / N

This reduces coincident partial peaks without randomizing the spectrum or requiring a seed.

Synthesis settings

SettingDefaultBehavior
Harmonics6Requested harmonic count; validated to 1–24.
Spectral tilt1.0Exponent in 1/k^tilt. It is not clamped.
Attack15 msLinear attack factor inside the note envelope.
Release150 msEach note's render window extends by this amount.
Decay per second0.8Exponential factor exp(−decay × elapsedTime).
Sampling rate44100 HzOutput sample rate; values below 8000 Hz are raised to 8000.
Peak ceiling0.95Valid range is (0,1]; invalid values revert to 0.95. The ceiling only attenuates.

Envelope

The note signal is multiplied by the parsed note amplitude and by a trapezoidal attack/release envelope combined with exponential decay. Release is part of the rendered note window, so overlapping release tails sum naturally with other notes.

Nyquist handling

The harmonic count is reduced independently for each note until every synthesized partial lies below Nyquist. If the fundamental itself is at or above Nyquist, that note is skipped rather than aliased. The Info report states the number of skipped notes.

The symbolic transformed-score plot still shows such notes because they remain part of the transformed note table; the rendered waveform and spectrogram omit any note skipped at the synthesis stage.

Peak ceiling

After all notes are summed, the script measures the absolute peak. If it exceeds Peak ceiling, the whole Sound is multiplied by ceiling / peak. Quiet renders are not amplified. The report shows raw peak, final peak, applied gain and RMS.

Visualization

When enabled, the Picture window contains five main regions:

  1. Original score — piano-roll-style rectangles for parsed source notes.
  2. Transformed score — the final symbolic note table after filter, pitch, chord, order and time stages. Rectangle color varies with the stored note amplitude.
  3. Rendered waveform — the mono additive result, using the measured final peak as the vertical range.
  4. Rendered spectrogram — displayed from 0 Hz to the smaller of Nyquist and 6000 Hz.
  5. Summary strip — source/result note counts, filtering/chord-op counts, tempo source, transformation chain, synthesis settings, duration, peak, gain, RMS and parser warning when applicable.

The original and transformed score panels use a shared pitch range and a shared time extent large enough to contain both the source score and the rendered result. Octave reference lines and note-name marks are drawn on the pitch axis.

Output & limitations

Output object

The script creates and leaves selected a mono Sound named:

<scoreName>_OM

For file input, scoreName is the filename without path and extension. For Strings input, it is the Strings object name. The output sample rate is the synthesis setting, normally 44.1 kHz.

Important limits

Quick start

  1. Run OM_Score_Transformer_v0.4.praat.
  2. Choose File on disk, or select one appropriate Strings object before running and choose Selected Strings object.
  3. Leave Tempo bpm at 0 to use the score's resolved tempo.
  4. Choose any filter, pitch, chord, order and time operations. Unused stages can remain at their neutral defaults.
  5. Leave Edit synthesis settings off for the default 6-harmonic, 44.1 kHz render, or enable it to open the advanced synthesis dialog.
  6. Run. The result is <scoreName>_OM; the Info window reports the exact chain and any parser/Nyquist warnings.