Module note_seq.musicxml_reader
MusicXML import.
Input wrappers for converting MusicXML into NoteSequence.
Expand source code
# Copyright 2021 The Magenta Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""MusicXML import.
Input wrappers for converting MusicXML into NoteSequence.
"""
from note_seq import musicxml_parser
from note_seq.protobuf import music_pb2
# Shortcut to CHORD_SYMBOL annotation type.
CHORD_SYMBOL = music_pb2.NoteSequence.TextAnnotation.CHORD_SYMBOL
class MusicXMLConversionError(Exception):
"""MusicXML conversion error handler."""
pass
def musicxml_to_sequence_proto(musicxml_document):
"""Convert MusicXML file contents to a NoteSequence proto.
Converts a MusicXML file encoded as a string into a NoteSequence proto.
Args:
musicxml_document: A parsed MusicXML file. This file has been parsed by
class MusicXMLDocument
Returns:
A NoteSequence proto.
Raises:
MusicXMLConversionError: An error occurred when parsing the MusicXML file.
"""
sequence = music_pb2.NoteSequence()
# Standard MusicXML fields.
sequence.source_info.source_type = (
music_pb2.NoteSequence.SourceInfo.SCORE_BASED)
sequence.source_info.encoding_type = (
music_pb2.NoteSequence.SourceInfo.MUSIC_XML)
sequence.source_info.parser = (
music_pb2.NoteSequence.SourceInfo.MAGENTA_MUSIC_XML)
# Populate header.
sequence.ticks_per_quarter = musicxml_document.midi_resolution
# Populate time signatures.
musicxml_time_signatures = musicxml_document.get_time_signatures()
for musicxml_time_signature in musicxml_time_signatures:
time_signature = sequence.time_signatures.add()
time_signature.time = musicxml_time_signature.time_position
time_signature.numerator = musicxml_time_signature.numerator
time_signature.denominator = musicxml_time_signature.denominator
# Populate key signatures.
musicxml_key_signatures = musicxml_document.get_key_signatures()
for musicxml_key in musicxml_key_signatures:
key_signature = sequence.key_signatures.add()
key_signature.time = musicxml_key.time_position
# The Key enum in music.proto does NOT follow MIDI / MusicXML specs
# Convert from MIDI / MusicXML key to music.proto key
music_proto_keys = [11, 6, 1, 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1]
key_signature.key = music_proto_keys[musicxml_key.key + 7]
if musicxml_key.mode == "major":
key_signature.mode = key_signature.MAJOR
elif musicxml_key.mode == "minor":
key_signature.mode = key_signature.MINOR
# Populate tempo changes.
musicxml_tempos = musicxml_document.get_tempos()
for musicxml_tempo in musicxml_tempos:
tempo = sequence.tempos.add()
tempo.time = musicxml_tempo.time_position
tempo.qpm = musicxml_tempo.qpm
# Populate notes from each MusicXML part across all voices
# Unlike MIDI import, notes are not sorted
sequence.total_time = musicxml_document.total_time_secs
for part_index, musicxml_part in enumerate(musicxml_document.parts):
part_info = sequence.part_infos.add()
part_info.part = part_index
part_info.name = musicxml_part.score_part.part_name
for musicxml_measure in musicxml_part.measures:
for musicxml_note in musicxml_measure.notes:
if not musicxml_note.is_rest:
note = sequence.notes.add()
note.part = part_index
note.voice = musicxml_note.voice
note.instrument = musicxml_note.midi_channel
note.program = musicxml_note.midi_program
note.start_time = musicxml_note.note_duration.time_position
# Fix negative time errors from incorrect MusicXML
note.start_time = max(note.start_time, 0)
note.end_time = note.start_time + musicxml_note.note_duration.seconds
note.pitch = musicxml_note.pitch[1] # Index 1 = MIDI pitch number
note.velocity = musicxml_note.velocity
durationratio = musicxml_note.note_duration.duration_ratio()
note.numerator = durationratio.numerator
note.denominator = durationratio.denominator
musicxml_chord_symbols = musicxml_document.get_chord_symbols()
for musicxml_chord_symbol in musicxml_chord_symbols:
text_annotation = sequence.text_annotations.add()
text_annotation.time = musicxml_chord_symbol.time_position
text_annotation.text = musicxml_chord_symbol.get_figure_string()
text_annotation.annotation_type = CHORD_SYMBOL
return sequence
def musicxml_file_to_sequence_proto(musicxml_file):
"""Converts a MusicXML file to a NoteSequence proto.
Args:
musicxml_file: A string path to a MusicXML file.
Returns:
A NoteSequence proto.
Raises:
MusicXMLConversionError: Invalid musicxml_file.
"""
try:
musicxml_document = musicxml_parser.MusicXMLDocument(musicxml_file)
except musicxml_parser.MusicXMLParseError as e:
raise MusicXMLConversionError(e)
return musicxml_to_sequence_proto(musicxml_document)
Functions
def musicxml_file_to_sequence_proto(musicxml_file)
-
Converts a MusicXML file to a NoteSequence proto.
Args
musicxml_file
- A string path to a MusicXML file.
Returns
A NoteSequence proto.
Raises
MusicXMLConversionError
- Invalid musicxml_file.
Expand source code
def musicxml_file_to_sequence_proto(musicxml_file): """Converts a MusicXML file to a NoteSequence proto. Args: musicxml_file: A string path to a MusicXML file. Returns: A NoteSequence proto. Raises: MusicXMLConversionError: Invalid musicxml_file. """ try: musicxml_document = musicxml_parser.MusicXMLDocument(musicxml_file) except musicxml_parser.MusicXMLParseError as e: raise MusicXMLConversionError(e) return musicxml_to_sequence_proto(musicxml_document)
def musicxml_to_sequence_proto(musicxml_document)
-
Convert MusicXML file contents to a NoteSequence proto.
Converts a MusicXML file encoded as a string into a NoteSequence proto.
Args
musicxml_document
- A parsed MusicXML file. This file has been parsed by class MusicXMLDocument
Returns
A NoteSequence proto.
Raises
MusicXMLConversionError
- An error occurred when parsing the MusicXML file.
Expand source code
def musicxml_to_sequence_proto(musicxml_document): """Convert MusicXML file contents to a NoteSequence proto. Converts a MusicXML file encoded as a string into a NoteSequence proto. Args: musicxml_document: A parsed MusicXML file. This file has been parsed by class MusicXMLDocument Returns: A NoteSequence proto. Raises: MusicXMLConversionError: An error occurred when parsing the MusicXML file. """ sequence = music_pb2.NoteSequence() # Standard MusicXML fields. sequence.source_info.source_type = ( music_pb2.NoteSequence.SourceInfo.SCORE_BASED) sequence.source_info.encoding_type = ( music_pb2.NoteSequence.SourceInfo.MUSIC_XML) sequence.source_info.parser = ( music_pb2.NoteSequence.SourceInfo.MAGENTA_MUSIC_XML) # Populate header. sequence.ticks_per_quarter = musicxml_document.midi_resolution # Populate time signatures. musicxml_time_signatures = musicxml_document.get_time_signatures() for musicxml_time_signature in musicxml_time_signatures: time_signature = sequence.time_signatures.add() time_signature.time = musicxml_time_signature.time_position time_signature.numerator = musicxml_time_signature.numerator time_signature.denominator = musicxml_time_signature.denominator # Populate key signatures. musicxml_key_signatures = musicxml_document.get_key_signatures() for musicxml_key in musicxml_key_signatures: key_signature = sequence.key_signatures.add() key_signature.time = musicxml_key.time_position # The Key enum in music.proto does NOT follow MIDI / MusicXML specs # Convert from MIDI / MusicXML key to music.proto key music_proto_keys = [11, 6, 1, 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 6, 1] key_signature.key = music_proto_keys[musicxml_key.key + 7] if musicxml_key.mode == "major": key_signature.mode = key_signature.MAJOR elif musicxml_key.mode == "minor": key_signature.mode = key_signature.MINOR # Populate tempo changes. musicxml_tempos = musicxml_document.get_tempos() for musicxml_tempo in musicxml_tempos: tempo = sequence.tempos.add() tempo.time = musicxml_tempo.time_position tempo.qpm = musicxml_tempo.qpm # Populate notes from each MusicXML part across all voices # Unlike MIDI import, notes are not sorted sequence.total_time = musicxml_document.total_time_secs for part_index, musicxml_part in enumerate(musicxml_document.parts): part_info = sequence.part_infos.add() part_info.part = part_index part_info.name = musicxml_part.score_part.part_name for musicxml_measure in musicxml_part.measures: for musicxml_note in musicxml_measure.notes: if not musicxml_note.is_rest: note = sequence.notes.add() note.part = part_index note.voice = musicxml_note.voice note.instrument = musicxml_note.midi_channel note.program = musicxml_note.midi_program note.start_time = musicxml_note.note_duration.time_position # Fix negative time errors from incorrect MusicXML note.start_time = max(note.start_time, 0) note.end_time = note.start_time + musicxml_note.note_duration.seconds note.pitch = musicxml_note.pitch[1] # Index 1 = MIDI pitch number note.velocity = musicxml_note.velocity durationratio = musicxml_note.note_duration.duration_ratio() note.numerator = durationratio.numerator note.denominator = durationratio.denominator musicxml_chord_symbols = musicxml_document.get_chord_symbols() for musicxml_chord_symbol in musicxml_chord_symbols: text_annotation = sequence.text_annotations.add() text_annotation.time = musicxml_chord_symbol.time_position text_annotation.text = musicxml_chord_symbol.get_figure_string() text_annotation.annotation_type = CHORD_SYMBOL return sequence
Classes
class MusicXMLConversionError (*args, **kwargs)
-
MusicXML conversion error handler.
Expand source code
class MusicXMLConversionError(Exception): """MusicXML conversion error handler.""" pass
Ancestors
- builtins.Exception
- builtins.BaseException