/music21/variant.py
Python | 2508 lines | 2410 code | 26 blank | 72 comment | 28 complexity | 9270b73819c2854efb356bd989b626e0 MD5 | raw file
Possible License(s): BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- # -*- coding: utf-8 -*-
- #-------------------------------------------------------------------------------
- # Name: variant.py
- # Purpose: Translate MusicXML and music21 objects
- #
- # Authors: Christopher Ariza
- # Evan Lynch
- # Michael Scott Cuthbert
- #
- # Copyright: Copyright 2012 Michael Scott Cuthbert and the music21 Project
- # License: LGPL
- #-------------------------------------------------------------------------------
- '''
- Contains :class:`~music21.variant.Variant` and its subclasses, as well as functions for merging
- and showing different variant streams. These functions and the variant class should only be
- used when variants of a score are the same length and contain the same measure structure at
- this time.
- '''
- import unittest
- import copy
- import difflib
- from music21 import base
- from music21 import exceptions21
- from music21 import common
- from music21 import stream
- from music21 import environment
- from music21 import note
- from music21 import search
- _MOD = "variant.py"
- environLocal = environment.Environment(_MOD)
- #-------Public Merge Functions
- def mergeVariants(streamX, streamY, variantName = 'variant', inPlace = False):
- '''
- Takes two streams objects or their derivatives (score, part, measure, etc.) which should be variant versions of the same stream,
- and merges them (determines differences and stores those differences as variant objects in streamX) via the appropriate merge
- function for their type. This will not know how to deal with scores meant for mergePartAsOssia(). If this is the intention, use
- that function instead.
-
-
- >>> aScore, vScore= stream.Score(), stream.Score()
-
- >>> ap1 = stream.Part(converter.parse(" a4 b c d e2 f2 g2 f4 g4 ", "4/4").makeMeasures())
- >>> vp1 = stream.Part(converter.parse(" a4 b c e e2 f2 g2 f4 a4 ", "4/4").makeMeasures())
-
- >>> ap2 = stream.Part(converter.parse(" a4 g f e f2 e2 d2 g4 f4 ", "4/4").makeMeasures())
- >>> vp2 = stream.Part(converter.parse(" a4 g f e f2 g2 f2 g4 d4 ", "4/4").makeMeasures())
-
- >>> aScore.insert(0.0, ap1)
- >>> aScore.insert(0.0, ap2)
- >>> vScore.insert(0.0, vp1)
- >>> vScore.insert(0.0, vp2)
-
- >>> mergedScore = variant.mergeVariants(aScore, vScore, variantName = 'docvariant', inPlace = False)
- >>> mergedScore.show('text')
- {0.0} <music21.stream.Part ...>
- {0.0} <music21.variant.Variant object of length 4.0>
- ...
- {0.0} <music21.stream.Part ...>
- {0.0} <music21.stream.Measure 1 offset=0.0>
- ...
- {4.0} <music21.variant.Variant object of length 8.0>
- {4.0} <music21.stream.Measure 2 offset=4.0>
- ...
- {4.0} <music21.bar.Barline style=final>
-
-
- >>> mergedPart = variant.mergeVariants(ap2, vp2, variantName = 'docvariant', inPlace = False)
- >>> mergedPart.show('text')
- {0.0} <music21.stream.Measure 1 offset=0.0>
- ...
- {4.0} <music21.variant.Variant object of length 8.0>
- {4.0} <music21.stream.Measure 2 offset=4.0>
- ...
- {4.0} <music21.bar.Barline style=final>
-
- >>> streamX = converter.parse("a4 b c d", "4/4")
- >>> streamY = converter.parse("a4 d c b", "4/4")
- >>> mergedStream = variant.mergeVariants(streamX, streamY, variantName = 'docvariant', inPlace = False)
- >>> mergedStream.show('text')
- {0.0} <music21.meter.TimeSignature 4/4>
- {0.0} <music21.note.Note A>
- {1.0} <music21.variant.Variant object of length 1.0>
- {1.0} <music21.note.Note B>
- {2.0} <music21.note.Note C>
- {3.0} <music21.variant.Variant object of length 1.0>
- {3.0} <music21.note.Note D>
-
- >>> streamY = converter.parse("a4 b c d e f g a", "4/4")
- >>> variant.mergeVariants(streamX, streamY, variantName = 'docvariant', inPlace = False)
- Traceback (most recent call last):
- ...
- VariantException: Could not determine what merging method to use. Try using a more specific merging function.
-
- '''
- classesX = streamX.classes
- if "Score" in classesX:
- return mergeVariantScores(streamX, streamY, variantName, inPlace = inPlace)
- elif "Part" in classesX or len(streamX.getElementsByClass("Measure")) > 0:
- return mergeVariantMeasureStreams(streamX, streamY, variantName, inPlace = inPlace)
- elif len(streamX.notesAndRests) > 0 and streamX.duration.quarterLength == streamY.duration.quarterLength:
- return mergeVariantsEqualDuration([streamX, streamY], [variantName], inPlace = inPlace)
- else:
- raise VariantException("Could not determine what merging method to use. Try using a more specific merging function.")
- def mergeVariantScores(aScore, vScore, variantName = 'variant', inPlace = False):
- '''
- Takes two scores and merges them with mergeVariantMeasureStreams, part-by-part.
-
-
- >>> aScore, vScore= stream.Score(), stream.Score()
-
- >>> ap1 = stream.Part(converter.parse(" a4 b c d e2 f2 g2 f4 g4 ", "4/4").makeMeasures())
- >>> vp1 = stream.Part(converter.parse(" a4 b c e e2 f2 g2 f4 a4 ", "4/4").makeMeasures())
-
- >>> ap2 = stream.Part(converter.parse(" a4 g f e f2 e2 d2 g4 f4 ", "4/4").makeMeasures())
- >>> vp2 = stream.Part(converter.parse(" a4 g f e f2 g2 f2 g4 d4 ", "4/4").makeMeasures())
-
- >>> aScore.insert(0.0, ap1)
- >>> aScore.insert(0.0, ap2)
- >>> vScore.insert(0.0, vp1)
- >>> vScore.insert(0.0, vp2)
-
- >>> mergedScores = variant.mergeVariantScores(aScore, vScore, variantName = 'docvariant', inPlace = False)
- >>> mergedScores.show('text')
- {0.0} <music21.stream.Part ...>
- {0.0} <music21.variant.Variant object of length 4.0>
- {0.0} <music21.stream.Measure 1 offset=0.0>
- {0.0} <music21.clef.TrebleClef>
- {0.0} <music21.meter.TimeSignature 4/4>
- {0.0} <music21.note.Note A>
- {1.0} <music21.note.Note B>
- {2.0} <music21.note.Note C>
- {3.0} <music21.note.Note D>
- {4.0} <music21.stream.Measure 2 offset=4.0>
- {0.0} <music21.note.Note E>
- {2.0} <music21.note.Note F>
- {8.0} <music21.variant.Variant object of length 4.0>
- {8.0} <music21.stream.Measure 3 offset=8.0>
- {0.0} <music21.note.Note G>
- {2.0} <music21.note.Note F>
- {3.0} <music21.note.Note G>
- {4.0} <music21.bar.Barline style=final>
- {0.0} <music21.stream.Part ...>
- {0.0} <music21.stream.Measure 1 offset=0.0>
- {0.0} <music21.clef.TrebleClef>
- {0.0} <music21.meter.TimeSignature 4/4>
- {0.0} <music21.note.Note A>
- {1.0} <music21.note.Note G>
- {2.0} <music21.note.Note F>
- {3.0} <music21.note.Note E>
- {4.0} <music21.variant.Variant object of length 8.0>
- {4.0} <music21.stream.Measure 2 offset=4.0>
- {0.0} <music21.note.Note F>
- {2.0} <music21.note.Note E>
- {8.0} <music21.stream.Measure 3 offset=8.0>
- {0.0} <music21.note.Note D>
- {2.0} <music21.note.Note G>
- {3.0} <music21.note.Note F>
- {4.0} <music21.bar.Barline style=final>
-
- '''
- if len(aScore.parts) != len(vScore.parts):
- raise VariantException("These scores do not have the same number of parts and cannot be merged.")
-
- if inPlace is True:
- returnObj = aScore
- else:
- returnObj = copy.deepcopy(aScore)
-
- for returnPart, vPart in zip(returnObj.parts, vScore.parts):
- mergeVariantMeasureStreams(returnPart, vPart, variantName, inPlace = True)
-
- if inPlace is False:
- return returnObj
-
- def mergeVariantMeasureStreams(streamX, streamY, variantName = 'variant', inPlace = False):
- '''
- Takes two streams of measures and returns a stream (new if inPlace is False) with the second
- merged with the first as variants. This function differs from mergeVariantsEqualDuration by
- dealing with streams that are of different length. This function matches measures that are
- exactly equal and creates variant objects for regions of measures that differ at all. If more
- refined variants are sought (with variation within the bar considered and related but different
- bars associated with each other), use variant.refineVariant().
-
- In this example, the second bar has been deleted in the second version, a new bar has been inserted between the
- original third and fourth bars, and two bars have been added at the end.
-
-
- >>> data1M1 = [('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter')]
- >>> data1M2 = [('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),('b', 'quarter')]
- >>> data1M3 = [('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
- >>> data1M4 = [('d', 'quarter'), ('g', 'eighth'), ('g', 'eighth'), ('a', 'quarter'), ('b', 'quarter')]
-
- >>> data2M1 = [('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter')]
- >>> data2M2 = [('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
- >>> data2M3 = [('e', 'quarter'), ('g', 'eighth'), ('g', 'eighth'), ('a', 'quarter'), ('b', 'quarter')]
- >>> data2M4 = [('d', 'quarter'), ('g', 'eighth'), ('g', 'eighth'), ('a', 'quarter'), ('b', 'quarter')]
- >>> data2M5 = [('f', 'eighth'), ('c', 'quarter'), ('a', 'eighth'), ('a', 'quarter'), ('b', 'quarter')]
- >>> data2M6 = [('g', 'quarter'), ('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
-
- >>> data1 = [data1M1, data1M2, data1M3, data1M4]
- >>> data2 = [data2M1, data2M2, data2M3, data2M4, data2M5, data2M6]
- >>> stream1 = stream.Stream()
- >>> stream2 = stream.Stream()
- >>> mNumber = 1
- >>> for d in data1:
- ... m = stream.Measure()
- ... m.number = mNumber
- ... mNumber += 1
- ... for pitchName,durType in d:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- ... stream1.append(m)
- >>> mNumber = 1
- >>> for d in data2:
- ... m = stream.Measure()
- ... m.number = mNumber
- ... mNumber += 1
- ... for pitchName,durType in d:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- ... stream2.append(m)
- >>> #_DOCS_SHOW stream1.show()
-
-
- .. image:: images/variant_measuresStreamMergeStream1.*
- :width: 600
-
- >>> #_DOCS_SHOW stream2.show()
-
-
- .. image:: images/variant_measuresStreamMergeStream2.*
- :width: 600
- >>> mergedStream = variant.mergeVariantMeasureStreams(stream1, stream2, 'paris', inPlace = False)
- >>> mergedStream.show('text')
- {0.0} <music21.stream.Measure 1 offset=0.0>
- {0.0} <music21.note.Note A>
- {1.0} <music21.note.Note B>
- {1.5} <music21.note.Note C>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note A>
- {4.0} <music21.variant.Variant object of length 0.0>
- {4.0} <music21.stream.Measure 2 offset=4.0>
- {0.0} <music21.note.Note B>
- {0.5} <music21.note.Note C>
- {1.0} <music21.note.Note A>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {8.0} <music21.stream.Measure 3 offset=8.0>
- {0.0} <music21.note.Note C>
- {1.0} <music21.note.Note D>
- {2.0} <music21.note.Note E>
- {3.0} <music21.note.Note E>
- {12.0} <music21.variant.Variant object of length 4.0>
- {12.0} <music21.stream.Measure 4 offset=12.0>
- {0.0} <music21.note.Note D>
- {1.0} <music21.note.Note G>
- {1.5} <music21.note.Note G>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {16.0} <music21.variant.Variant object of length 8.0>
-
- >>> mergedStream.variants[0].replacementDuration
- 4.0
- >>> mergedStream.variants[1].replacementDuration
- 0.0
-
- >>> parisStream = mergedStream.activateVariants('paris', inPlace = False)
- >>> parisStream.show('text')
- {0.0} <music21.stream.Measure 1 offset=0.0>
- {0.0} <music21.note.Note A>
- {1.0} <music21.note.Note B>
- {1.5} <music21.note.Note C>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note A>
- {4.0} <music21.variant.Variant object of length 4.0>
- {4.0} <music21.stream.Measure 2 offset=4.0>
- {0.0} <music21.note.Note C>
- {1.0} <music21.note.Note D>
- {2.0} <music21.note.Note E>
- {3.0} <music21.note.Note E>
- {8.0} <music21.variant.Variant object of length 0.0>
- {8.0} <music21.stream.Measure 3 offset=8.0>
- {0.0} <music21.note.Note E>
- {1.0} <music21.note.Note G>
- {1.5} <music21.note.Note G>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {12.0} <music21.stream.Measure 4 offset=12.0>
- {0.0} <music21.note.Note D>
- {1.0} <music21.note.Note G>
- {1.5} <music21.note.Note G>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {16.0} <music21.variant.Variant object of length 0.0>
- {16.0} <music21.stream.Measure 5 offset=16.0>
- {0.0} <music21.note.Note F>
- {0.5} <music21.note.Note C>
- {1.5} <music21.note.Note A>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {20.0} <music21.stream.Measure 6 offset=20.0>
- {0.0} <music21.note.Note G>
- {1.0} <music21.note.Note D>
- {2.0} <music21.note.Note E>
- {3.0} <music21.note.Note E>
-
- >>> parisStream.variants[0].replacementDuration
- 0.0
- >>> parisStream.variants[1].replacementDuration
- 4.0
- >>> parisStream.variants[2].replacementDuration
- 8.0
- '''
- if inPlace is True:
- returnObj = streamX
- else:
- returnObj = copy.deepcopy(streamX)
-
- regions = _getRegionsFromStreams(returnObj,streamY)
- for regionType, xRegionStartMeasure, xRegionEndMeasure, yRegionStartMeasure, yRegionEndMeasure in regions: # Note that the 'end' measure indices are 1 greater than the 0-indexed number of the measure.
- if xRegionStartMeasure >= len(returnObj.getElementsByClass('Measure')):
- startOffset = returnObj.duration.quarterLength #This deals with insertion at the end case where returnObj.measure(xRegionStartMeasure+1) does not exist.
- else:
- startOffset = returnObj.measure(xRegionStartMeasure+1).getOffsetBySite(returnObj)
- if regionType is 'equal':
- #yRegion = streamY.measures(yRegionStartMeasure+1, yRegionEndMeasure)
- continue #Do nothing
- elif regionType is 'replace':
- xRegion = returnObj.measures(xRegionStartMeasure+1, xRegionEndMeasure)
- replacementDuration = xRegion.duration.quarterLength
- yRegion = streamY.measures(yRegionStartMeasure+1, yRegionEndMeasure)
- elif regionType is 'delete':
- xRegion = returnObj.measures(xRegionStartMeasure+1, xRegionEndMeasure)
- replacementDuration = xRegion.duration.quarterLength
- yRegion = None
- elif regionType is 'insert':
- yRegion = streamY.measures(yRegionStartMeasure+1, yRegionEndMeasure)
- replacementDuration = 0.0
- addVariant(returnObj, startOffset, yRegion, variantName = variantName, replacementDuration = replacementDuration)
-
- if inPlace is True:
- return
- else:
- return returnObj
- def mergeVariantsEqualDuration(streams, variantNames, inPlace = False):
- '''
- Pass this function a list of streams (they must be of the same length or a VariantException will be raised).
- It will return a stream which merges the differences between the streams into variant objects keeping the
- first stream in the list as the default. If inPlace is True, the first stream in the list will be modified,
- otherwise a new stream will be returned. Pass a list of names to associate variants with their sources, if this list
- does not contain an entry for each non-default variant, naming may not behave properly. Variants that have the
- same differences from the default will be saved as separate variant objects (i.e. more than once under different names).
- Also, note that a streams with bars of differing lengths will not behave properly.
-
-
- >>> stream1 = stream.Stream()
- >>> stream2paris = stream.Stream()
- >>> stream3london = stream.Stream()
- >>> data1 = [('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),
- ... ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),
- ... ('b', 'quarter'), ('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter')]
- >>> data2 = [('a', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('g', 'quarter'),
- ... ('b', 'eighth'), ('c', 'quarter'), ('a', 'eighth'), ('a', 'quarter'),
- ... ('b', 'quarter'), ('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter')]
- >>> data3 = [('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),
- ... ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),
- ... ('c', 'quarter'), ('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter')]
- >>> for pitchName,durType in data1:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... stream1.append(n)
- >>> for pitchName,durType in data2:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... stream2paris.append(n)
- >>> for pitchName,durType in data3:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... stream3london.append(n)
- >>> mergedStreams = variant.mergeVariantsEqualDuration([stream1, stream2paris, stream3london], ['paris', 'london'])
- >>> mergedStreams.show('t')
- {0.0} <music21.note.Note A>
- {1.0} <music21.variant.Variant object of length 1.0>
- {1.0} <music21.note.Note B>
- {1.5} <music21.note.Note C>
- {2.0} <music21.note.Note A>
- {3.0} <music21.variant.Variant object of length 1.0>
- {3.0} <music21.note.Note A>
- {4.0} <music21.note.Note B>
- {4.5} <music21.variant.Variant object of length 1.5>
- {4.5} <music21.note.Note C>
- {5.0} <music21.note.Note A>
- {6.0} <music21.note.Note A>
- {7.0} <music21.variant.Variant object of length 1.0>
- {7.0} <music21.note.Note B>
- {8.0} <music21.note.Note C>
- {9.0} <music21.variant.Variant object of length 2.0>
- {9.0} <music21.note.Note D>
- {10.0} <music21.note.Note E>
-
- >>> mergedStreams.activateVariants('london').show('t')
- {0.0} <music21.note.Note A>
- {1.0} <music21.variant.Variant object of length 1.0>
- {1.0} <music21.note.Note B>
- {1.5} <music21.note.Note C>
- {2.0} <music21.note.Note A>
- {3.0} <music21.variant.Variant object of length 1.0>
- {3.0} <music21.note.Note A>
- {4.0} <music21.note.Note B>
- {4.5} <music21.variant.Variant object of length 1.5>
- {4.5} <music21.note.Note C>
- {5.0} <music21.note.Note A>
- {6.0} <music21.note.Note A>
- {7.0} <music21.variant.Variant object of length 1.0>
- {7.0} <music21.note.Note C>
- {8.0} <music21.note.Note C>
- {9.0} <music21.variant.Variant object of length 2.0>
- {9.0} <music21.note.Note D>
- {10.0} <music21.note.Note E>
-
- If the streams contain parts and measures, the merge function will iterate through them and determine
- and store variant differences within each measure/part.
-
- >>> stream1 = stream.Stream()
- >>> stream2 = stream.Stream()
- >>> data1M1 = [('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter')]
- >>> data1M2 = [('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),('b', 'quarter')]
- >>> data1M3 = [('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
- >>> data2M1 = [('a', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('g', 'quarter')]
- >>> data2M2 = [('b', 'eighth'), ('c', 'quarter'), ('a', 'eighth'), ('a', 'quarter'), ('b', 'quarter')]
- >>> data2M3 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
- >>> data1 = [data1M1, data1M2, data1M3]
- >>> data2 = [data2M1, data2M2, data2M3]
- >>> tempPart = stream.Part()
- >>> for d in data1:
- ... m = stream.Measure()
- ... for pitchName,durType in d:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- ... tempPart.append(m)
- >>> stream1.append(tempPart)
- >>> tempPart = stream.Part()
- >>> for d in data2:
- ... m = stream.Measure()
- ... for pitchName,durType in d:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- ... tempPart.append(m)
- >>> stream2.append(tempPart)
- >>> mergedStreams = variant.mergeVariantsEqualDuration([stream1, stream2], ['paris'])
- >>> mergedStreams.show('t')
- {0.0} <music21.stream.Part ...>
- {0.0} <music21.stream.Measure 0 offset=0.0>
- {0.0} <music21.note.Note A>
- {1.0} <music21.variant.Variant object of length 1.0>
- {1.0} <music21.note.Note B>
- {1.5} <music21.note.Note C>
- {2.0} <music21.note.Note A>
- {3.0} <music21.variant.Variant object of length 1.0>
- {3.0} <music21.note.Note A>
- {4.0} <music21.stream.Measure 0 offset=4.0>
- {0.0} <music21.note.Note B>
- {0.5} <music21.variant.Variant object of length 1.5>
- {0.5} <music21.note.Note C>
- {1.0} <music21.note.Note A>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {8.0} <music21.stream.Measure 0 offset=8.0>
- {0.0} <music21.note.Note C>
- {1.0} <music21.variant.Variant object of length 3.0>
- {1.0} <music21.note.Note D>
- {2.0} <music21.note.Note E>
- {3.0} <music21.note.Note E>
- >>> #_DOCS_SHOW mergedStreams.show()
-
-
- .. image:: images/variant_measuresAndParts.*
- :width: 600
-
-
- >>> for p in mergedStreams.getElementsByClass('Part'):
- ... for m in p.getElementsByClass('Measure'):
- ... m.activateVariants('paris', inPlace = True)
- >>> mergedStreams.show('t')
- {0.0} <music21.stream.Part ...>
- {0.0} <music21.stream.Measure 0 offset=0.0>
- {0.0} <music21.note.Note A>
- {1.0} <music21.variant.Variant object of length 1.0>
- {1.0} <music21.note.Note B>
- {2.0} <music21.note.Note A>
- {3.0} <music21.variant.Variant object of length 1.0>
- {3.0} <music21.note.Note G>
- {4.0} <music21.stream.Measure 0 offset=4.0>
- {0.0} <music21.note.Note B>
- {0.5} <music21.variant.Variant object of length 1.5>
- {0.5} <music21.note.Note C>
- {1.5} <music21.note.Note A>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {8.0} <music21.stream.Measure 0 offset=8.0>
- {0.0} <music21.note.Note C>
- {1.0} <music21.variant.Variant object of length 3.0>
- {1.0} <music21.note.Note B>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note A>
- >>> #_DOCS_SHOW mergedStreams.show()
-
-
- .. image:: images/variant_measuresAndParts2.*
- :width: 600
-
- If barlines do not match up, an exception will be thrown. Here two streams that are identical
- are merged, except one is in 3/4, the other in 4/4. This throws an exception.
-
- >>> streamDifferentMeasures = stream.Stream()
- >>> dataDiffM1 = [('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter')]
- >>> dataDiffM2 = [ ('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter')]
- >>> dataDiffM3 = [('a', 'quarter'), ('b', 'quarter'), ('c', 'quarter')]
- >>> dataDiffM4 = [('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
- >>> dataDiff = [dataDiffM1, dataDiffM2, dataDiffM3, dataDiffM4]
- >>> streamDifferentMeasures.insert(0.0, meter.TimeSignature('3/4'))
- >>> tempPart = stream.Part()
- >>> for d in dataDiff:
- ... m = stream.Measure()
- ... for pitchName,durType in d:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- ... tempPart.append(m)
- >>> streamDifferentMeasures.append(tempPart)
- >>> mergedStreams = variant.mergeVariantsEqualDuration([stream1, streamDifferentMeasures], ['paris'])
- Traceback (most recent call last):
- ...
- VariantException: _mergeVariants cannot merge streams which are of different lengths
- '''
-
- if inPlace is True:
- returnObj = streams[0]
- else:
- returnObj = copy.deepcopy(streams[0])
- variantNames.insert(0, None) # Adds a None element at beginning (corresponding to default variant streams[0])
- while len(streams) > len(variantNames): # Adds Blank names if too few
- variantNames.append(None)
- while len(streams) < len(variantNames): # Removes extra names
- variantNames.pop
-
- zipped = zip(streams,variantNames)
-
- for s,variantName in zipped[1:]:
- if returnObj.highestTime != s.highestTime:
- raise VariantException('cannot merge streams of different lengths')
- returnObjParts = returnObj.getElementsByClass('Part')
- if len(returnObjParts) != 0: # If parts exist, iterate through them.
- sParts = s.getElementsByClass('Part')
- for i in range(len(returnObjParts)):
- returnObjPart = returnObjParts[i]
- sPart = sParts[i]
-
- returnObjMeasures = returnObjPart.getElementsByClass('Measure')
- if len(returnObjMeasures) != 0: # If measures exist and parts exist, iterate through them both.
- for j in range(len(returnObjMeasures)):
- returnObjMeasure = returnObjMeasures[j]
- sMeasure = sPart.getElementsByClass('Measure')[j]
- _mergeVariants(returnObjMeasure,sMeasure,variantName = variantName, inPlace = True)
-
- else: # If parts exist but no measures.
- _mergeVariants(returnObjPart,sPart,variantName = variantName, inPlace = True)
- else:
- returnObjMeasures = returnObj.getElementsByClass('Measure')
- if len(returnObjMeasures) != 0: #If no parts, but still measures, iterate through them.
- for j in range(len(returnObjMeasures)):
- returnObjMeasure = returnObjMeasures[j]
- sMeasure = s.getElementsByClass('Measure')[j]
- _mergeVariants(returnObjMeasure,sMeasure, variantName = variantName, inPlace = True)
- else: # If no parts and no measures.
- _mergeVariants(returnObj,s,variantName = variantName, inPlace = True)
-
- return returnObj
- def mergePartAsOssia(mainpart, ossiapart, ossiaName, inPlace = False, compareByMeasureNumber = False, recurseInMeasures = False):
- '''
- Some MusicXML files are generated with full parts that have only a few non-rest measures instead of ossia parts, such as those
- created by Sibelius 7. This function
- takes two streams (mainpart and ossiapart), the second interpreted as an ossia. It outputs a stream with the ossia part merged into the stream as a
- group of variants.
-
- If compareByMeasureNumber is True, then the ossia measures will be paired with the measures in the mainpart that have the
- same measure.number. Otherwise, they will be paired by offset. In most cases these should have the same result.
-
- Note that this method has no way of knowing if a variant is supposed to be a different duration than the segment of stream which it replaces
- because that information is not contained in the format of score this method is designed to deal with.
-
-
- >>> mainstream = converter.parse(" A4 B4 C4 D4 E1 F2 E2 E8 F8 F4 G2 G2 G4 F4 F4 F4 F4 F4 G1 ", "4/4")
- >>> ossiastream = converter.parse(" r1 r1 r1 E4 E4 F4 G4 r1 F2 F2 r1 ", "4/4")
- >>> mainstream.makeMeasures(inPlace = True)
- >>> ossiastream.makeMeasures(inPlace = True)
-
- >>> # mainstream.__class__ = stream.Part
- >>> mainpart = stream.Part()
- >>> for m in mainstream:
- ... mainpart.insert(m.offset, m)
- >>> ossiapart = stream.Part()
- >>> for m in ossiastream:
- ... ossiapart.insert(m.offset, m)
-
- >>> s = stream.Stream()
- >>> s.insert(0.0, ossiapart)
- >>> s.insert(0.0, mainpart)
- >>> #_DOCS_SHOW s.show()
-
- >>> mainpartWithOssiaVariantsFT = variant.mergePartAsOssia(mainpart, ossiapart,
- ... ossiaName = 'Parisian Variant',
- ... inPlace = False,
- ... compareByMeasureNumber = False,
- ... recurseInMeasures = True)
- >>> mainpartWithOssiaVariantsTT = variant.mergePartAsOssia(mainpart, ossiapart,
- ... ossiaName = 'Parisian Variant',
- ... inPlace = False,
- ... compareByMeasureNumber = True,
- ... recurseInMeasures = True)
- >>> mainpartWithOssiaVariantsFF = variant.mergePartAsOssia(mainpart, ossiapart,
- ... ossiaName = 'Parisian Variant',
- ... inPlace = False,
- ... compareByMeasureNumber = False,
- ... recurseInMeasures = False)
- >>> mainpartWithOssiaVariantsTF = variant.mergePartAsOssia(mainpart, ossiapart,
- ... ossiaName = 'Parisian Variant',
- ... inPlace = False,
- ... compareByMeasureNumber = True,
- ... recurseInMeasures = False)
-
- >>> mainpartWithOssiaVariantsFT.show('text') == mainpartWithOssiaVariantsTT.show('text')
- {0.0} <music21.stream.Measure ...
- True
-
- >>> mainpartWithOssiaVariantsFF.show('text') == mainpartWithOssiaVariantsFT.show('text')
- {0.0} <music21.stream.Measure ...
- True
-
- >>> mainpartWithOssiaVariantsFT.show('text')
- {0.0} <music21.stream.Measure 1 offset=0.0>
- ...
- {12.0} <music21.stream.Measure 4 offset=12.0>
- {0.0} <music21.variant.Variant object of length 3.0>
- {0.0} <music21.note.Note E>
- {0.5} <music21.note.Note F>
- {1.0} <music21.note.Note F>
- {2.0} <music21.note.Note G>
- {16.0} <music21.stream.Measure 5 offset=16.0>
- ...
- {20.0} <music21.stream.Measure 6 offset=20.0>
- {0.0} <music21.variant.Variant object of length 4.0>
- {0.0} <music21.note.Note F>
- {1.0} <music21.note.Note F>
- {2.0} <music21.note.Note F>
- {3.0} <music21.note.Note F>
- ...
-
- >>> mainpartWithOssiaVariantsFF.activateVariants('Parisian Variant').show('text')
- {0.0} <music21.stream.Measure 1 offset=0.0>
- ...
- {12.0} <music21.variant.Variant object of length 4.0>
- {12.0} <music21.stream.Measure 4 offset=12.0>
- {0.0} <music21.note.Note E>
- {1.0} <music21.note.Note E>
- {2.0} <music21.note.Note F>
- {3.0} <music21.note.Note G>
- {16.0} <music21.stream.Measure 5 offset=16.0>
- ...
- {20.0} <music21.variant.Variant object of length 4.0>
- {20.0} <music21.stream.Measure 6 offset=20.0>
- {0.0} <music21.note.Note F>
- {2.0} <music21.note.Note F>
- ...
-
- '''
- if inPlace is True:
- returnObj = mainpart
- else:
- returnObj = copy.deepcopy(mainpart)
-
- if compareByMeasureNumber is True:
- for ossiaMeasure in ossiapart.getElementsByClass("Measure"):
- if len(ossiaMeasure.notes) > 0: #If the measure is not just rests
- ossiaNumber = ossiaMeasure.number
- returnMeasure = returnObj.measure(ossiaNumber)
- if recurseInMeasures is True:
- mergeVariantsEqualDuration([returnMeasure, ossiaMeasure], [ossiaName], inPlace = True)
- else:
- ossiaOffset = returnMeasure.getOffsetBySite(returnObj)
- addVariant(returnObj, ossiaOffset, ossiaMeasure, variantName = ossiaName, variantGroups = None, replacementDuration = None)
- else:
- for ossiaMeasure in ossiapart.getElementsByClass("Measure"):
- if len(ossiaMeasure.notes) > 0: #If the measure is not just rests
- ossiaOffset = ossiaMeasure.getOffsetBySite(ossiapart)
- if recurseInMeasures is True:
- returnMeasure = returnObj.getElementsByOffset(ossiaOffset, classList = [stream.Measure])[0]
- mergeVariantsEqualDuration([returnMeasure, ossiaMeasure], [ossiaName], inPlace = True)
- else:
- addVariant(returnObj, ossiaOffset, ossiaMeasure, variantName = ossiaName, variantGroups = None, replacementDuration = None)
-
- if inPlace is True:
- return
- else:
- return returnObj
- #------ Public Helper Functions
- def addVariant(s, startOffset, sVariant, variantName = None, variantGroups = None, replacementDuration = None):
- '''
- Takes a stream, the location of the variant to be added to that stream (startOffset), the content of the
- variant to be added (sVariant), and the duration of the section of the stream which the variant
- replaces (replacementDuration). If replacementDuration is 0, this is an insertion. If sVariant is
- None, this is a deletion.
-
-
- >>> data1M1 = [('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter')]
- >>> data1M3 = [('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
- >>> data1M2 = [('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),('b', 'quarter')]
- >>> data1 = [data1M1, data1M2, data1M3]
- >>> tempPart = stream.Part()
- >>> stream1 = stream.Stream()
- >>> for d in data1:
- ... m = stream.Measure()
- ... for pitchName,durType in d:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- ... stream1.append(m)
- >>> data2M2 = [('b', 'eighth'), ('c', 'quarter'), ('a', 'eighth'), ('a', 'quarter'), ('b', 'quarter')]
- >>> stream2 = stream.Stream()
- >>> m = stream.Measure()
- >>> for pitchName,durType in data2M2:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- >>> stream2.append(m)
- >>> variant.addVariant(stream1, 4.0, stream2, variantName = 'rhythmic switch', replacementDuration = 4.0)
- >>> stream1.show('text')
- {0.0} <music21.stream.Measure 0 offset=0.0>
- {0.0} <music21.note.Note A>
- {1.0} <music21.note.Note B>
- {1.5} <music21.note.Note C>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note A>
- {4.0} <music21.variant.Variant object of length 4.0>
- {4.0} <music21.stream.Measure 0 offset=4.0>
- {0.0} <music21.note.Note B>
- {0.5} <music21.note.Note C>
- {1.0} <music21.note.Note A>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {8.0} <music21.stream.Measure 0 offset=8.0>
- {0.0} <music21.note.Note C>
- {1.0} <music21.note.Note D>
- {2.0} <music21.note.Note E>
- {3.0} <music21.note.Note E>
-
- >>> stream1 = stream.Stream()
- >>> stream1.repeatAppend(note.Note('e'), 6)
- >>> variant1 = variant.Variant()
- >>> variant1.repeatAppend(note.Note('f'), 3)
- >>> startOffset = 3.0
- >>> variant.addVariant(stream1, startOffset, variant1, variantName = 'paris', replacementDuration = 3.0)
- >>> stream1.show('text')
- {0.0} <music21.note.Note E>
- {1.0} <music21.note.Note E>
- {2.0} <music21.note.Note E>
- {3.0} <music21.variant.Variant object of length 0.0>
- {3.0} <music21.note.Note E>
- {4.0} <music21.note.Note E>
- {5.0} <music21.note.Note E>
- '''
- tempVariant = Variant()
-
- if variantGroups is not None:
- tempVariant.groups = variantGroups
- if variantName is not None:
- tempVariant.groups.append(variantName)
-
- tempVariant.replacementDuration = replacementDuration
-
- if sVariant is None: #deletion
- pass
- else: #replacement or insertion
- if "Measure" in sVariant.classes: #sVariant is a measure put it in a variant and insert.
- tempVariant.append(sVariant)
- else: #sVariant is not a measure
- sVariantMeasures = sVariant.getElementsByClass('Measure')
- if sVariantMeasures == []: # If there are no measures, work element-wise
- for e in sVariant:
- offset = e.getOffsetBySite(sVariant)+startOffset
- tempVariant.insert(offset, e)
- else: # if there are measures work measure-wise
- for m in sVariantMeasures:
- tempVariant.append(m)
- s.insert(startOffset, tempVariant)
- def refineVariant(s, sVariant, inPlace = False):
- '''
- Given a stream and variant contained in that stream, returns a stream with that variant 'refined.'
- It is refined in the sense that, (with the best estimates) measures which have been determined
- to be related are merged within the measure. Suppose a four-bar phrase in a piece is a slightly
- different five-bar phrase in a variant. In the variant, every F# has been replaced by an F,
- and the last bar is repeated. Given this streams, mergeVariantMeasureStreams would return
- the first stream with a single variant object containing the entire 5 bars of the variant.
- Calling refineVariant on this stream and that variant object would result in a variant object
- in the measures for each F#/F pair, and a variant object containing the added bar at the end.
- For a more detailed explanation of how similar measures are properly associated with each other
- look at the documentation for _getBestListandScore
-
- Note that this code does not work properly yet.
-
-
- >>> v = variant.Variant()
- >>> variantDataM1 = [('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),('b', 'quarter')]
- >>> variantDataM2 = [('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
- >>> variantData = [variantDataM1, variantDataM2]
- >>> for d in variantData:
- ... m = stream.Measure()
- ... for pitchName,durType in d:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- ... v.append(m)
- >>> v.groups = ['paris']
- >>> v.replacementDuration = 8.0
-
- >>> s = stream.Stream()
- >>> streamDataM1 = [('a', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('g', 'quarter')]
- >>> streamDataM2 = [('b', 'eighth'), ('c', 'quarter'), ('a', 'eighth'), ('a', 'quarter'), ('b', 'quarter')]
- >>> streamDataM3 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
- >>> streamDataM4 = [('c', 'quarter'), ('b', 'quarter'), ('a', 'quarter'), ('a', 'quarter')]
- >>> streamData = [streamDataM1, streamDataM2, streamDataM3, streamDataM4]
- >>> for d in streamData:
- ... m = stream.Measure()
- ... for pitchName,durType in d:
- ... n = note.Note(pitchName)
- ... n.duration.type = durType
- ... m.append(n)
- ... s.append(m)
- >>> s.insert(4.0, v)
- >>> variant.refineVariant(s, v, inPlace = True)
- >>> s.show('text')
- {0.0} <music21.stream.Measure 0 offset=0.0>
- {0.0} <music21.note.Note A>
- {1.0} <music21.note.Note B>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note G>
- {4.0} <music21.stream.Measure 0 offset=4.0>
- {0.0} <music21.note.Note B>
- {0.5} <music21.variant.Variant object of length 1.5>
- {0.5} <music21.note.Note C>
- {1.5} <music21.note.Note A>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note B>
- {8.0} <music21.stream.Measure 0 offset=8.0>
- {0.0} <music21.note.Note C>
- {1.0} <music21.variant.Variant object of length 3.0>
- {1.0} <music21.note.Note B>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note A>
- {12.0} <music21.stream.Measure 0 offset=12.0>
- {0.0} <music21.note.Note C>
- {1.0} <music21.note.Note B>
- {2.0} <music21.note.Note A>
- {3.0} <music21.note.Note A>
-
- '''
- # stream that will be returned
- if not (sVariant in s.variants):
- raise VariantException('%s not found in stream %s.' % (sVariant, s))
-
- if inPlace is True:
- returnObject = s
- variantRegion = sVariant
- else:
- sVariantIndex = s.variants.index(sVariant)
-
- returnObject = copy.deepcopy(s)
- variantRegion = returnObject.variants(sVariantIndex)
-
-
- # useful parameters from variant and its location
- variantGroups = sVariant.groups
- replacementDuration = sVariant.replacementDuration
- startOffset = sVariant.getOffsetBySite(s)
- #endOffset = replacementDuration+startOffset
-
- # region associated with the given variant in the stream
- returnRegion = variantRegion.replacedElements(returnObject)
- # associating measures in variantRegion to those in returnRegion -> This is done via 0 indexed lists corresponding to measures
- returnRegionMeasureList = [i for i in range(len(returnRegion))]
- badnessDict = {}
- listDict = {}
- variantMeasureList, unused_badness = _getBestListandScore(returnRegion, variantRegion, badnessDict, listDict)
-
- # badness is a measure of how different the streams are. The list returned, variantMeasureList, minimizes that quantity.
-
- # mentioned lists are compared via difflib for optimal edit regions (equal, delete, insert, replace)
- sm = difflib.SequenceMatcher()
- sm.set_seqs(returnRegionMeasureList, variantMeasureList)
- regions = sm.get_opcodes()
-
- # each region is processed for variants.
- for regionType, returnStart, returnEnd, variantStart, variantEnd in regions:
- startOffset = returnRegion[returnStart].getOffsetBySite(returnRegion)
- #endOffset = returnRegion[returnEnd-1].getOffsetBySite(returnRegion)+returnRegion[returnEnd-1].duration.quarterLength
- if regionType is 'equal':
- returnSubRegion = returnRegion.measures(returnStart+1, returnEnd)
- variantSubRegion = variantRegion.measures(variantStart+1, variantEnd)
- mergeVariantsEqualDuration([returnSubRegion, variantSubRegion], variantGroups, inPlace = True)
- continue
-
- elif regionType is 'replace':
- returnSubRegion = returnRegion.measures(returnStart+1, returnEnd)
- replacementDuration = returnSubRegion.duration.quarterLength
- variantSubRegion = variantRegion.measures(variantStart+1, variantEnd)
- elif regionType is 'delete':
- returnSubRegion = returnRegion.measures(returnStart+1, returnEnd)
- replacementDuration = returnSubRegion.duration.quarterLength
- variantSubRegion = None
- elif regionType is 'insert':
- variantSubRegion = variantRegion.measures(variantStart+1, variantEnd)
- replacementDuration = 0.0
- addVariant(returnRegion, startOffset, variantSubRegion, variantGroups = variantGroups, replacementDuration = replacementDuration)
-
- returnObject.remove(variantRegion) # The original variant object has been replaced by more refined variant objects and so should be deleted.
- if inPlace:
- return
- else:
- return returnObject
- def _mergeVariantMeasureStreamsCarefully(streamX, streamY, variantName, inPlace = False):
- '''
- There seem to be some problems with this function and it isn't well tested.
- It is not recommended to use it at this time.
-
- '''
- # stream that will be returned
- if inPlace is True:
- returnObject = streamX
- variantObject = streamY
- else:
- returnObject = copy.deepcopy(streamX)
- variantObject = copy.deepcopy(streamY)
-
-
- # associating measures in variantRegion to those in returnRegion -> This is done via 0 indexed lists corresponding to measures
- returnObjectMeasureList = [i for i in range(len(returnObject.getElementsByClass("Measure")))]
- badnessDict = {}
- listDict = {}
- variantObjectMeasureList, unused_badness = _getBestListandScore(returnObject.getElementsByClass("Measure"), variantObject.getElementsByClass("Measure"), badnessDict, listDict)
-
- # badness is a measure of how different the streams are. The list returned, variantMeasureList, minimizes that quantity.
-
- # mentioned lists are compared via difflib for optimal edit regions (equal, delete, insert, replace)
- sm = difflib.SequenceMatcher()
- sm.set_seqs(returnObjectMeasureList, variantObjectMeasureList)
- regions = sm.get_opcodes()
-
- # each region is processed for variants.
- for regionType, returnStart, returnEnd, variantStart, variantEnd in regions:
- startOffset = returnObject.measure(returnStart+1).getOffsetBySite(returnObject)
- #endOffset = returnObject.measure(returnEnd-1).getOffsetBySite(returnObject)+returnObject.measure(returnEnd-1).duration.quarterLength
- if regionType is 'equal':
- returnSubRegion = returnObject.measures(returnStart+1, returnEnd)
- variantSubRegion = variantObject.measures(variantStart+1, variantEnd)
- mergeVariantMeasureStreams(returnSubRegion, variantSubRegion, [variantName], inPlace = True)
- continue
-
- elif regionType is 'replace':
- returnSubRegion = returnObject.measures(returnStart+1, returnEnd)
- replacementDuration = returnSubRegion.duration.quarterLength
- variantSubRegion = variantObject.measures(variantStart+1, variantEnd)
- elif regionType is 'delete':
- returnSubRegion = returnObject.measures(returnStart+1, returnEnd)
- replacementDuration = returnSubRegion.duration.quarterLength
- variantSubRegion = None
- elif regionType is 'insert':
- variantSubRegion = variantObject.measures(variantStart+1, variantEnd)
- replacementDuration = 0.0
- addVariant(returnObject, startOffset, variantSubRegion, variantGroups = [variantName], replacementDuration = replacementDuration)
-
- if inPlace:
- return
- else:
- return returnObject
- def getMeasureHashes(s):
- '''
- Takes in a stream containing measures and returns a list of hashes, one for each measure. Currently
- implemented with search.translateStreamToString()
-
-
- >>> s = converter.parse("c4 d8. e16 FF4 a'4 b-2", "2/4")
- >>> sm = s.makeMeasures()
- >>> hashes = variant.getMeasureHashes(sm)
- >>> hashes
- ['<P>K@<', ')PQP', 'FZ']
- '''
- hashes = []
- if isinstance(s, list):
- for m in s:
- hashes.append(search.translateStreamToString(m.notesAndRests))
- return hashes
- else:
- for m in s.getElementsByClass('Measure'):
- hashes.append(search.translateStreamToString(m.notesAndRests))
- return hashes
- #----- Private Helper Functions
- def _getBestListandScore(streamX, streamY, badnessDict, listDict, isNone = False, streamXindex = -1, streamYindex = -1):
- '''
- This is a recursive function which makes a map between two related streams of measures.
- It is designed for streams of measures that contain few if any measures that are actually
- identical and that have a different number of measures (within reason). For example,
- if one stream has 10 bars of eighth notes and the second stream has the same ten bars
- of eighth notes except with some dotted rhythms mixed in and the fifth bar is repeated.
- The first, streamX, is the reference stream. This function returns a list of
- integers with length len(streamY) which maps each measure of StreamY to the measure
- in streamX it is most likely associated with. For example, if the returned list is
- [0, 2, 3, 'addedBar', 4]. This indicates that streamY is most similar to streamX
- after the second bar of streamX has been removed and a new bar inserted between
- bars 4 and 5. Note that this list has measures 0-indexed. This function generates this map by
- minimizing the difference or 'badness' for the sequence of measures on the whole as determined
- by the helper function _simscore which compares measures for similarity. 'addedBar' appears
- in the list where this function has determined that the bar appearing
- in streamY does not have a counterpart in streamX anywhere and is an insertion.
-
-
- >>> badnessDict = {}
- >>> listDict = {}
- >>> stream1 = stream.Stream()
- >>> stream2 = stream.Stream()
-
- >>> data1M1 = [('a', 'quarter'), ('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter')]
- >>> data1M2 = [('b', 'eighth'), ('c', 'eighth'), ('a', 'quarter'), ('a', 'quarter'),('b', 'quarter')]
- >>> data1M3 = [('c', 'quarter'), ('d', 'quarter'), ('e', 'quarter'), ('e', 'quarter')]
-
- >>> data2M1 = [('a', 'quarter'), ('b', 'quarter'), ('c', 'quarter'), ('g#', 'quarter')]
- >>> da…
Large files files are truncated, but you can click here to view the full file