Repository: cuthbertLab/music21
Pull request #95 (2015-05-02 16:04:52)

musicxml mid-measure clefs
Hi,
I solved the issue #28 for the case where I needed it. I don't know if it solves the problem in all respects (I don't know enough about the music21 internals for that), but for my cases it works.
Hope this helps.
Best,
Frank




Patch:
diff --git a/music21/musicxml/fromMxObjects.py b/music21/musicxml/fromMxObjects.py
index dfa9278e1..606cdcc66 100644
--- a/music21/musicxml/fromMxObjects.py
+++ b/music21/musicxml/fromMxObjects.py
@@ -2496,6 +2496,11 @@ def mxToMeasure(mxMeasure, spannerBundle=None, inputM21=None, lastMeasureInfo=No
             addToStaffReference(mxObj, h, staffReference)
             m._insertCore(offsetMeasureNote, h)
 
+        elif isinstance(mxObj, mxObjects.Clef):
+            cl = mxClefToClef(mxObj)
+            addToStaffReference(mxObj, cl, staffReference)
+            m._insertCore(offsetMeasureNote, cl)
+
 
     #environLocal.printDebug(['staffReference', staffReference])
     # if we have voices and/or if we used backup/forward, we may have
diff --git a/music21/musicxml/toMxObjects.py b/music21/musicxml/toMxObjects.py
index 5e8da3057..12f2fe34b 100644
--- a/music21/musicxml/toMxObjects.py
+++ b/music21/musicxml/toMxObjects.py
@@ -2083,6 +2083,11 @@ def measureToMx(m, spannerBundle=None, mxTranspose=None):
                 mxDirection.offset = mxOffset
                 # place at zero position, as offset value determines horizontal position, not location amongst notes
                 mxMeasure.insert(0, mxDirection)
+            elif 'Clef' in classes and obj.offset != 0.0:
+                # clef within bar
+                clefAttributes = mxObjects.Attributes()
+                clefAttributes.clefList = [clefToMxClef(obj)]
+                mxMeasure.componentList.append(clefAttributes)
             else: # other objects may have already been added
                 pass
                 #environLocal.printDebug(['mesureToMx of Measure is not processing', obj])
diff --git a/music21/musicxml/xmlHandler.py b/music21/musicxml/xmlHandler.py
index 635b900cb..253c5c6fa 100644
--- a/music21/musicxml/xmlHandler.py
+++ b/music21/musicxml/xmlHandler.py
@@ -930,7 +930,12 @@ class Handler(xml.sax.ContentHandler):
                 musicxmlMod.BeatType(self._currentTag.charData))
 
         elif name == 'clef':
-            self._mxObjs['attributes'].clefList.append(self._mxObjs['clef'])
+            if len(self._mxObjs['measure']) == 0:
+                # clef at beginning of measure into attributes
+                self._mxObjs['attributes'].clefList.append(self._mxObjs['clef'])
+            else:
+                # mid-measure clefs into measure-components
+                self._mxObjs['measure'].componentList.append(self._mxObjs['clef'])
 
         elif name == 'multiple-rest':
             self._mxObjs['measure-style'].multipleRest = self._currentTag.charData


Test patch:
diff --git a/music21/musicxml/testPrimitive.py b/music21/musicxml/testPrimitive.py
index c73c6a85b..bb0cda184 100644
--- a/music21/musicxml/testPrimitive.py
+++ b/music21/musicxml/testPrimitive.py
@@ -17681,12 +17681,80 @@ class Test(unittest.TestCase):
         for testMaterial in ALL:
             dummy = converter.parse(testMaterial)
 
+#-------------------------------------------------------------------------------
+
+class TestMidMeasureClef1(unittest.TestCase):
+    """ Tests if there are mid-mesure clefs clefs: single staff """
+
+    def runTest(self):
+        pass
+
+    def testBasic(self):
+        from music21 import stream, note, clef, musicxml, converter, meter
+
+        orig_stream = stream.Stream()
+        orig_stream.append(meter.TimeSignature("4/4"))
+        orig_stream.append(clef.TrebleClef())
+        orig_stream.repeatAppend(note.Note("C4"), 2)
+        orig_stream.append(clef.BassClef())
+        orig_stream.repeatAppend(note.Note("C4"), 2)
+        orig_clefs = orig_stream.flat.getElementsByClass('Clef')
+
+        xml = musicxml.m21ToString.fromStream(orig_stream)
+        
+        new_stream = converter.parse(xml)
+        new_clefs = new_stream.flat.getElementsByClass('Clef')
 
+        assert len(new_clefs) == len(orig_clefs)
+        assert [c.offset for c in new_clefs] == [c.offset for c in orig_clefs]
+        assert [c.classes for c in new_clefs] == [c.classes for c in orig_clefs]
+
+class TestMidMeasureClef2(unittest.TestCase):
+    """ Tests if there are mid-mesure clefs clefs: multiple staves """
+
+    def runTest(self):
+        pass
+
+    def testBasic(self):
+        from music21 import stream, note, clef, musicxml, converter, meter
+
+        orig_stream = stream.Stream()
+        orig_stream.append(stream.Part())
+        orig_stream.append(stream.Part())
+        orig_stream.append(meter.TimeSignature("3/4"))
+        
+        for item in [clef.TrebleClef(), note.Note("C4"), clef.BassClef(), \
+            note.Note("C4"), note.Note("C4")]:
+            orig_stream[0].append(item)
+
+        for item in [clef.BassClef(), note.Note("C4"), note.Note("C4"), \
+            clef.TrebleClef(), note.Note("C4")]:
+            orig_stream[1].append(item)
+
+        orig_clefs = [staff.flat.getElementsByClass('Clef') for staff in
+            orig_stream.getElementsByClass('Part')]
+
+        xml = musicxml.m21ToString.fromStream(orig_stream)
+
+        new_stream = converter.parse(xml)
+        new_clefs = [staff.flat.getElementsByClass('Clef') for staff in
+            new_stream.getElementsByClass('Part')]
+
+        assert [len(clefs) for clefs in new_clefs] == \
+          [len(clefs) for clefs in orig_clefs]
+        assert [c.offset for c in clefs for clefs in new_clefs] == \
+          [c.offset for c in clefs for clefs in orig_clefs]
+        assert [c.classes for c in clefs for clefs in new_clefs] == \
+          [c.classes for c in clefs for clefs in orig_clefs]
+
+#-------------------------------------------------------------------------------
 
 if __name__ == "__main__":
     # sys.arg test options will be used in mainTest()
     import music21
     music21.mainTest(Test)
+    music21.mainTest(TestMidMeasureClef1)
+    music21.mainTest(TestMidMeasureClef2)
 
 
 #------------------------------------------------------------------------------