Extracting information from Musicxml
Asked Answered
G

2

8

I am new to programming and Python, but a lot of my current research concerns extracting data from musicxml files. I have a piece of music and want to extract the number of accidentals that occur in a piece that do not form part of the key signature. I have no idea how to do this, please could someone help? Here's an example of one measure from the musicxml file I am looking at:

<measure number='19'>
        <print new-system='no'/>
        <note>
            <rest/>
            <duration>768</duration>
            <voice>1</voice>
            <type>quarter</type>
            <staff>1</staff>
        </note>
        <backup>
            <duration>768</duration>
        </backup>
        <note>
            <pitch>
                <step>E</step>
                <octave>4</octave>
            </pitch>
            <duration>2304</duration>
            <tie type='start'/>
            <voice>2</voice>
            <type>half</type>
            <dot/>
            <staff>1</staff>
            <notations>
                <tied type='start'/>
                <slur type='stop' number='1'/>
            </notations>
        </note>
        <backup>
            <duration>1536</duration>
        </backup>
        <note>
            <pitch>
                <step>E</step>
                <alter>3</alter>
                <octave>3</octave>
            </pitch>
            <duration>1536</duration>
            <voice>1</voice>
            <type>half</type>
            <staff>1</staff>
        </note>
        <note>
            <chord/>
            <pitch>
                <step>G</step>
                <alter>4</alter>
                <octave>3</octave>
            </pitch>
            <duration>1536</duration>
            <voice>1</voice>
            <type>half</type>
            <staff>1</staff>
        </note>
        <backup>
            <duration>2304</duration>
        </backup>
        <note>
            <pitch>
                <step>E</step>
                <octave>2</octave>
            </pitch>
            <duration>2304</duration>
            <voice>5</voice>
            <type>half</type>
            <dot/>
            <staff>2</staff>
        </note>
    </measure>

The problem translates to searching through the musicxml file and counting the number of times

<pitch>
   <step>*</step>
   <alter>**</alter>
       ...

occurs where * is not (F or C) and also finding the number of times that * is F or C and is not followed by the <alter> tag.

Any help or advice would be much appreciated!

Grantgranta answered 30/1, 2013 at 12:37 Comment(0)
A
8

I can't help with Python details, but I have two MusicXML-related suggestions:

1) Your question is phrased in terms of accidentals, but your code focuses on the alter element. The alter element is used for pitch alteration; the accidental element is what is used for written accidentals. Which one are you looking for? The duality between how much sounds and how it appears in notation is common in MusicXML, and important to understand for doing research with MusicXML files.

2) If you are new to programming and Python, I would suggest using a higher-level toolkit designed expressly for musicology with good MusicXML support. You will move the problem domain up to a higher level which should let you make progress a lot faster. The obvious choice for this is the music21 toolkit, which is also written in Python. There's lots more information at http://web.mit.edu/music21/.

Good luck with your research!

Avestan answered 1/2, 2013 at 7:17 Comment(2)
Thanks Michael, I have found music21 really useful. In my musicxml files the alter tag is used instead of accidentals. I'm trying to find all the notes that are accidentals with respect to the given key signature. Is there a quick way to do this in music21? what about pieces that have a change of key signature somewhere within the piece?Grantgranta
Hi 2025161: in music21 you could iterate over each note with .flat.notes (assuming no chords; or .getElementsByClass('Note') if there are) and then for each Note object compare its .pitch.accidental object to n.getContextByClass('KeySignature').accidentalByStep(n.pitch.step) and find places where they're different. Note that this simple code runs in O(n^2) time; you could cache your KeySignature objects to make it run more quickly on large scores.Apposite
A
3

python has an xml.dom module that allows you to quickly navigate through xml files. If you have any experience with web development, it's very similar to javascript's document object model.

from xml.dom.minidom import parse, parseString

def get_step(note):
    stepNode = note.getElementsByTagName("step")[0]
    #get the text from the Text Node within the <step>,
    #and convert it from unicode to ascii
    return str(stepNode.childNodes[0].nodeValue)

def get_alter(note):
    alters = note.getElementsByTagName("alter")
    if len(alters) == 0:
        return None
    return alters[0]

def is_rest(note):
    return len(note.getElementsByTagName("rest")) > 0

def is_accidental(note):
    return get_alter(note) != None

dom = parse("data.xml")

notes = dom.getElementsByTagName("note")
#rests don't have steps or alters, so we don't care about them. Filter them out.
notes = filter(lambda note: not is_rest(note), notes)

#compile a list of notes of all accidentals (notes with <alter> tags)
accidentals = filter(is_accidental, notes)
#remove notes that are F or C
accidentals_that_are_not_f_or_c = filter(lambda note: get_step(note) not in ["F", "C"], accidentals)

#compile a list of notes that don't contain the alter tag
non_accidentals = filter(lambda note: not is_accidental(note), notes)
#remove notes that are not F or C
non_accidentals_that_are_f_or_c = filter(lambda note: get_step(note) in ["F", "C"], non_accidentals)

print "Accidental notes that are not F or C:"
if len(accidentals_that_are_not_f_or_c) == 0:
    print "(None found)"
else:
    for note in accidentals_that_are_not_f_or_c:
        print get_step(note)

print "Non-accidental notes that are F or C:"
if len(non_accidentals_that_are_f_or_c) == 0:
    print "(None found)"
else:
    for note in non_accidentals_that_are_f_or_c:
        print get_step(note), get_step(note) in ["F", "C"]

output:

Accidental notes that are not F or C:
E
G
Non-accidental notes that are F or C:
(None found)
Aphrodisiac answered 30/1, 2013 at 13:9 Comment(1)
Thank you for this, however that code gives me the same result as if I were to import lxml.etree and use musicxml.xpath('count(//alter)'). I want to compile a list of note names of all the accidentals (under the <alter> tag) and then remove those that are F or C. I also need to compile a list of those notes that don't contain the <alter> tag and remove those notes that are not F or C. Forgive me for being ignorant, I'm really new to this.Grantgranta

© 2022 - 2024 — McMap. All rights reserved.