This task is part of project08 which is due at 23:00 EST on 2025-11-04.
You have the option to work with a partner on this task if you wish. Working with a partner requires more work to coordinate schedules, but if you work together and make sure that you are both understanding the code you write, you will make progress faster and learn more.
You can download the starter code for this task using this link.
You can submit this task using this link.
Put all of your work for this task into the file
measureMunger.py
(which is provided among the starter files)
This project is very similar the shapeSorter
task which uses turtle to draw pictures instead of
wavesynth to generate audio. You only have to do one of the two.
In this task you will define a number of short functions divided into several groups, to help you practice using list comprehensions and sorting:
makeRunsListmakeTriplesListmakeListWithDirectionsmakeMeasuresListoddNotesMeasuresmeasuresInDirectionmeasuresThatReachgetDirectionAndNotesgetPitchSpanIntervalAndNotessortByIntervalsortByDirectionsortBySpanaddMeasureSequenceAlmost all of these functions can be written in just one or two lines of code. We have provided a few helper functions that you will need for several of your functions:
pitchReachedFromC4 which can compute the pitch of the last note in
a sequence of notes if the sequence starts at C4 and uses the
specified interval between notes, number of notes, and direction
(True for up or False for down). fit a polygon inside it.pitchSpanFromC4 which can compute the ratio between the highest and
lowest notes of a note sequence using a specific interval, number of
notes, and direction (again assuming it starts at C4).customMeasure which can add notes for a custom measure to the
current track.tuneTest which will use many of your functions together to create a
complex track (we have also provided a test_measureMunger.py file
for local testing).As usual, each function you write must be documented.
A key concept in this project is a specification: a tuple of values
that specify the arguments for a function. The function in this case is
customMeasure, which has three parameters: the interval between notes,
the number of notes, and whether the notes increase (True) or decrease
(False) in pitch. Each specification that we use will be a tuple
with 3 elements holding the interval, notes, and direction. For example,
the tuple:
(1, 3, False)
specifies that a sequence of 3 notes should be created, each one scale
rung below the last (they'd be going up if the last value had been
True instead). If we have a specification stored in a variable, we
can use indexing or unpacking to access the individual parts, like so:
spec = (1, 3, True)
# Indexing to get parts of tuple:
interval = spec[0]
noteCount = spec[1]
goingUp = spec[2]
# Unpacking to get parts of tuple:
interval, noteCount, goingUp = spec
When the time comes to actually call customMeasure using a
specification, we will need to unpack it so that we can provide each
argument to customMeasure one at a time. If we have individual
variables and want to create a specification, we can just use
parentheses and commas, like so:
myDirection = True
myInterval = 2
myNotes = 4
mySpec = (myInterval, myNotes, myDirection) # order is important here
Most functions in this assignment will deal with specifications one way or another.
For each list-building function, you will define a function that takes a few parameters and builds and returns a list of specification tuples (see above) using a list comprehension:
makeRunsList takes a list of
note counts and makes
specifications that each use an interval of 1 and a direction of
True (going up), and then the number of notes is one item from the
note counts list. The resulting list of
specifications will have the
same length as the counts list it's given.
makeTriplesList takes a single direction boolean and a list of
intervals and returns a
specifications list that uses 3 notes and the specified direction
for each entry, with one of the interval values from the intervals
list. This means that the number of
specifications will match the number of intervals provided.makeListWithDirections takes a list of partial specifications which
are pairs containing an interval and a number of notes, along with a
single direction boolean as its second
argument. It must
return a specifications list where each entry uses the interval
and notes count from one of the partial specifications and then
includes the common direction as the third
element.makeMeasuresList takes three lists: one of intervals, another of
note counts, and a third of direction
booleans. It can assume that
each list will have the same length, but it needs to use
len and
range along
with a list comprehension to zip all three lists together into a
single specifications list, where
each entry takes an interval, a note count, and a direction from the
same position in all three input lists.As usual, refer to the examples section for concrete examples of how these functions should work.
For each list-filtering function, you will define a function that takes a few parameters and returns a new list of specification tuples (see above) based on filtering out some of the provided specifications using a list comprehension :
oddNotesMeasures just takes a specifications
list and returns a new
list that includes only specifications with an odd number-of-notes
value.measuresInDirection takes a specifications list and a direction
boolean and returns a
new list including only specifications that use the required
direction.measuresThatReach takes a specifications list and a pitch
value and returns a new
list including only specifications whose highest note is at or above
that pitch target when starting from pitch
C4. It must
call
pitchReachedFromC4
in order to figure that out, and it should do so inside its list
comprehension.As you know, in order to do any custom sorting we need to define a key function that will take in one element being sorted and return a "key" object that determines how that element will get sorted (the rest of the sorting process is automatic).
For example, if our key function was the following:
def characterKey(character):
if character == 'C':
return 1
elif character == 'S':
return 2
elif character == '1':
return 100
then if we called .sort using that function like this:
letters = ['1', 'C', '1', 'S', '1']
letters.sort(key=characterKey)
print(letters)
We'd see the following output:
['C', 'S', '1', '1', '1']
That's because Python orders the key values from lowest to highest,
and then arranges the original items based on where their key values
end up. Since our characterKey function returns the number 1 as the
key for the letter 'C', and that's smaller than the keys for the other
letters we use, that letter ends up first in the result, etc.
As an additional reminder, to break ties when sorting, if the key value is a tuple or some other kind of sequence (including a string), Python tries to sort using just the first element of the sequence, then breaks ties based on the ordering of the second elements of each key, then breaks double-ties based on the third elements, and so on. So you can use tuples as the return value from a key function to specify both primary sort order (using first element of the tuple) and secondary sort orders in case of ties (using subsequent elements).
For the next part, you'll define the key functions that we'll use to sort with:
getDirectionAndNotes takes a single specification
tuple (see
above) and returns a tuple containing first the direction
boolean and then the number of
notes. Because this is a
common point of confusion, remember that getDirectionAndNotes does
NOT need to use a
loop because it
only needs to process a single specification, and it will get
applied to a list of specifications automatically during the
sorting process.
getPitchSpanIntervalAndNotes takes a single specification
tuple and
returns a tuple containing first the ratio between the top and
bottom pitch of the measure when based at C4, then the interval
value, and finally the number of notes in the
measure. It must call
the provided
pitchSpanFromC4
function and as with getDirectionAndNotes it does NOT need to use
a loop.Now that you have helper functions ready, you'll define some sorting
functions which can be used to re-order specification lists. These
functions will NOT return anything because they will directly
re-arrange the specifications list they are
given
. Each of these functions
must call the .sort list
method
, and they
may not use any loops
(which aren't necessary).
sortByInterval takes two parameters: a list of specifications and a
boolean indicating whether to sort in ascending or descending
order. It sorts the
specifications by their natural
order without using a key
function, sorting by interval value, breaking ties by number of
notes, and breaking double-ties by direction boolean.sortByDirection just takes a specifications
list and sorts it by
direction boolean (with False before True by default), breaking ties
by the number of notes. It will
use the getDirectionAndNotes function you defined earlier.sortBySpan just takes a specifications
list and sorts it by pitch span,,
breaking ties by side length and then number of
sides. It will use the
getPitchSpanIntervalAndNotes function you defined earlier.For the final part of this project, you have to implement a
addMeasureSequence function which takes a specifications list, a
starting pitch, and a start
time and adds notes for each
specified measure one after the other starting from the given start
pitch & starting at the given start
time.
The whole process starts at the given pitch and starting time within the
track, but you won't modify pitches or times between calls to
customMeasure: just let the pitch changes from one measure dictate the
starting pitch of the next so that they flow together.
addMeasureSequence must do the following:
sort (it
doesn't need to, this is just a reminder of that).setPitch exactly
once to
establish the starting pitch.setTime exactly
once to
establish the starting time.customMeasure
to actually add notes; it does NOT need to use addNote directly.return.makeRunsList examples
These examples show what makeRunsList should return.
In []:Out[]:makeRunsList([4, 2, 3])In []:[(1, 4, True), (1, 2, True), (1, 3, True)]Out[]:makeRunsList([1, 1, 2, 3])In []:[(1, 1, True), (1, 1, True), (1, 2, True), (1, 3, True)]Out[]:makeRunsList([8, 9, 10])[(1, 8, True), (1, 9, True), (1, 10, True)]
makeTriplesList examples
These examples show what makeTriplesList should return.
In []:Out[]:makeTriplesList(True, [2, 1, 3])In []:[(2, 3, True), (1, 3, True), (3, 3, True)]Out[]:makeTriplesList(False, [4])In []:[(4, 3, False)]Out[]:makeTriplesList(True, [2, 2, 4, 2])[(2, 3, True), (2, 3, True), (4, 3, True), (2, 3, True)]
makeListWithDirections examples
These examples show what makeListWithDirections should return.
In []:Out[]:makeListWithDirections([(8, 1), (6, 2), (4, 3)], True)In []:[(8, 1, True), (6, 2, True), (4, 3, True)]Out[]:makeListWithDirections([(3, 5), (3, 4), (4, 4)], False)In []:[(3, 5, False), (3, 4, False), (4, 4, False)]Out[]:makeListWithDirections([(5, 2), (1, 3), (7, 2), (5, 3)], True)[(5, 2, True), (1, 3, True), (7, 2, True), (5, 3, True)]
makeMeasuresList examples
These examples show what makeMeasuresList should return.
In []:Out[]:makeMeasuresList([1, 2, 4], [8, 6, 3], [False, False, True])In []:[(1, 8, False), (2, 6, False), (4, 3, True)]Out[]:makeMeasuresList( [3, 1, 2, 3, 2, 3, 4], [4, 6, 8, 6, 4, 5, 5], [False, False, True, True, False, True, False] )In []:[ (3, 4, False), (1, 6, False), (2, 8, True), (3, 6, True), (2, 4, False), (3, 5, True), (4, 5, False), ]Out[]:makeMeasuresList([4], [1], [False])[(4, 1, False)]
oddNotesMeasures examples
These examples show what oddNotesMeasures should return.
In []:Out[]:oddNotesMeasures( [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] )In []:[(1, 3, True), (1, 5, False)]Out[]:oddNotesMeasures( [ (1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True), ] )In []:[ (1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True), ]Out[]:oddNotesMeasures( [ (3, 3, True), (1, 4, False), (5, 3, False), (1, 6, True), (2, 5, False), (3, 4, True), (5, 1, True), ] )[(3, 3, True), (5, 3, False), (2, 5, False), (5, 1, True)]
measuresInDirection examples
These examples show what measuresInDirection should return.
In []:Out[]:measuresInDirection( [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)], True )In []:[(1, 3, True), (1, 4, True), (1, 6, True)]Out[]:measuresInDirection( [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)], False )In []:[(1, 5, False), (1, 2, False)]Out[]:measuresInDirection( [ (1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True), ], True )[(1, 3, True), (3, 3, True), (5, 3, True), (6, 3, True)]
measuresThatReach examples
These examples show what measuresThatReach should return.
In []:Out[]:measuresThatReach( [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)], 293.672 )In []:[(1, 3, True), (1, 4, True), (1, 6, True)]Out[]:measuresThatReach( [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)], 392.0051 )In []:[(1, 4, True), (1, 6, True)]Out[]:measuresThatReach( [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)], 1046.528 )[]
getDirectionAndNotes examples
These examples show what getDirectionAndNotes should return.
In []:Out[]:getDirectionAndNotes((1, 3, True))In []:(True, 3)Out[]:getDirectionAndNotes((1, 4, True))In []:(True, 4)Out[]:getDirectionAndNotes((3, 3, True))(True, 3)
getPitchSpanIntervalAndNotes examples
These examples show what getPitchSpanIntervalAndNotes should return.
In []:Out[]:getPitchSpanIntervalAndNotes((1, 3, True))In []:(1.498307076876685, 1, 3)Out[]:getPitchSpanIntervalAndNotes((5, 3, True))In []:(8.000000000000028, 5, 3)Out[]:getPitchSpanIntervalAndNotes((5, 3, False))(7.9999999999999964, 5, 3)
addMeasureSequence examples
These examples show what notes should added when addMeasureSequence is f called.
In []:Audio In []:addMeasureSequence( [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)], 261.632, 0 )Audio In []:addMeasureSequence( [ (1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True), ], 329.6357, 0 )AudioaddMeasureSequence( [ (3, 3, True), (1, 4, False), (5, 3, False), (1, 6, True), (2, 5, False), (3, 4, True), (5, 1, True), ], 880.0216, 0.5 )
sortByInterval examples
These examples show how the order of the target list should be modified when calling sortByInterval. Remember that sortByInterval is NOT supposed to use return but should instead modify the order of the list it's given using the .sort method. The prints here are part of our testing setup; your sortByInterval function does NOT need to print anything.
In []:Printsspecifications = [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] print("Before sorting we have:") print(specifications) returned = sortByInterval(specifications, True) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] After sorting we get: [(1, 2, False), (1, 3, True), (1, 4, True), (1, 5, False), (1, 6, True)]In []:Printsspecifications = [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] print("Before sorting we have:") print(specifications) returned = sortByInterval(specifications, False) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] After sorting we get: [(1, 6, True), (1, 5, False), (1, 4, True), (1, 3, True), (1, 2, False)]In []:Printsspecifications = [(1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True)] print("Before sorting we have:") print(specifications) returned = sortByInterval(specifications, True) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True)] After sorting we get: [(1, 3, True), (2, 3, False), (3, 3, True), (4, 3, False), (5, 3, True), (6, 3, True)]In []:Printsspecifications = [(1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True)] print("Before sorting we have:") print(specifications) returned = sortByInterval(specifications, False) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True)] After sorting we get: [(6, 3, True), (5, 3, True), (4, 3, False), (3, 3, True), (2, 3, False), (1, 3, True)]
sortByDirection examples
These examples show how the order of the target list should be modified when calling sortByDirection. Remember that sortByDirection is NOT supposed to use return but should instead modify the order of the list it's given using the .sort method. The prints here are part of our testing setup; your sortByDirection function does NOT need to print anything.
In []:Printsspecifications = [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] print("Before sorting we have:") print(specifications) returned = sortByDirection(specifications) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] After sorting we get: [(1, 2, False), (1, 5, False), (1, 3, True), (1, 4, True), (1, 6, True)]In []:Printsspecifications = [(3, 3, True), (1, 4, False), (5, 3, False), (1, 6, True), (2, 5, False), (3, 4, True), (5, 1, True)] print("Before sorting we have:") print(specifications) returned = sortByDirection(specifications) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(3, 3, True), (1, 4, False), (5, 3, False), (1, 6, True), (2, 5, False), (3, 4, True), (5, 1, True)] After sorting we get: [(5, 3, False), (1, 4, False), (2, 5, False), (5, 1, True), (3, 3, True), (3, 4, True), (1, 6, True)]In []:Printsspecifications = [(5, 1, True), (3, 4, True), (2, 5, False), (1, 6, True), (5, 3, False), (1, 4, False), (3, 3, True)] print("Before sorting we have:") print(specifications) returned = sortByDirection(specifications) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(5, 1, True), (3, 4, True), (2, 5, False), (1, 6, True), (5, 3, False), (1, 4, False), (3, 3, True)] After sorting we get: [(5, 3, False), (1, 4, False), (2, 5, False), (5, 1, True), (3, 3, True), (3, 4, True), (1, 6, True)]
sortBySpan examples
These examples show how the order of the target list should be modified when calling sortBySpan. Remember that sortBySpan is NOT supposed to use return but should instead modify the order of the list it's given using the .sort method. The prints here are part of our testing setup; your sortBySpan function does NOT need to print anything.
In []:Printsspecifications = [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] print("Before sorting we have:") print(specifications) returned = sortBySpan(specifications) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(1, 3, True), (1, 4, True), (1, 5, False), (1, 2, False), (1, 6, True)] After sorting we get: [(1, 2, False), (1, 3, True), (1, 4, True), (1, 5, False), (1, 6, True)]In []:Printsspecifications = [(1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True)] print("Before sorting we have:") print(specifications) returned = sortBySpan(specifications) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(1, 3, True), (3, 3, True), (2, 3, False), (5, 3, True), (4, 3, False), (6, 3, True)] After sorting we get: [(1, 3, True), (2, 3, False), (3, 3, True), (4, 3, False), (5, 3, True), (6, 3, True)]In []:Printsspecifications = [(3, 3, True), (1, 4, False), (5, 3, False), (1, 6, True), (2, 5, False), (3, 4, True), (5, 1, True)] print("Before sorting we have:") print(specifications) returned = sortBySpan(specifications) if returned != None: print('Oops, the sorting function had a return value!') print("After sorting we get:") print(specifications)Before sorting we have: [(3, 3, True), (1, 4, False), (5, 3, False), (1, 6, True), (2, 5, False), (3, 4, True), (5, 1, True)] After sorting we get: [(1, 4, False), (5, 1, True), (1, 6, True), (3, 3, True), (2, 5, False), (3, 4, True), (5, 3, False)]
tuneTest example
Although the code for tuneTest was provided with the starter code, it won't work until all of your other functions are working. This shows what the result should sound like when tuneTest is run after your project is complete. You can hear the results of the various sorting functions used by tuneTest.
In []:AudiotuneTest()
makeRunsList must return the correct result
makeRunsList function is run must match the solution result.makeTriplesList must return the correct result
makeTriplesList function is run must match the solution result.makeListWithDirections must return the correct result
makeListWithDirections function is run must match the solution result.makeMeasuresList must return the correct result
makeMeasuresList function is run must match the solution result.oddNotesMeasures must return the correct result
oddNotesMeasures function is run must match the solution result.measuresInDirection must return the correct result
measuresInDirection function is run must match the solution result.measuresThatReach must return the correct result
measuresThatReach function is run must match the solution result.getDirectionAndNotes must return the correct result
getDirectionAndNotes function is run must match the solution result.getPitchSpanIntervalAndNotes must return the correct result
getPitchSpanIntervalAndNotes function is run must match the solution result.sortByInterval must modify its first argument correctly
sortByInterval with a test argument, and check to
make sure that it modifies its first argument correctly. We will
also make sure that the function returns None.sortByDirection must modify its first argument correctly
sortByDirection with a test argument, and check to
make sure that it modifies its first argument correctly. We will
also make sure that the function returns None.sortBySpan must modify its first argument correctly
sortBySpan with a test argument, and check to
make sure that it modifies its first argument correctly. We will
also make sure that the function returns None.addMeasureSequence must produce the correct note sequence
addMeasureSequence is called must match the solution notes in terms of timing, instruments, pitches, and volumes.tuneTest must produce the correct note sequence
tuneTest is called must match the solution notes in terms of timing, instruments, pitches, and volumes.makeRunsList with 1 parameter
def to define makeRunsList with 1 parametermakeRunsList with 1 parameter, use a comprehension in at least one place.makeTriplesList with 2 parameters
def to define makeTriplesList with 2 parametersmakeTriplesList with 2 parameters, use a comprehension in at least one place.makeListWithDirections with 2 parameters
def to define makeListWithDirections with 2 parametersmakeListWithDirections with 2 parameters, use a comprehension in at least one place.makeMeasuresList with 3 parameters
def to define makeMeasuresList with 3 parametersmakeMeasuresList with 3 parameters, use a comprehension in at least one place.len
makeMeasuresList with 3 parameters, call len in at least one place.range
makeMeasuresList with 3 parameters, call range in at least one place.oddNotesMeasures with 1 parameter
def to define oddNotesMeasures with 1 parameteroddNotesMeasures with 1 parameter, use a comprehension in at least one place.measuresInDirection with 2 parameters
def to define measuresInDirection with 2 parametersmeasuresInDirection with 2 parameters, use a comprehension in at least one place.measuresThatReach with 2 parameters
def to define measuresThatReach with 2 parametersmeasuresThatReach with 2 parameters, use a comprehension in at least one place.pitchReachedFromC4
measuresThatReach with 2 parameters, call pitchReachedFromC4 in at least one place.measuresThatReach with 2 parameters
def to define measuresThatReach with 2 parametersmeasuresThatReach with 2 parameters, use a comprehension in at least one place.pitchReachedFromC4
measuresThatReach with 2 parameters, call pitchReachedFromC4 in at least one place.getDirectionAndNotes with 1 parameter
def to define getDirectionAndNotes with 1 parametergetDirectionAndNotes with 1 parameter, do not use any kind of loop.getPitchSpanIntervalAndNotes with 1 parameter
def to define getPitchSpanIntervalAndNotes with 1 parametergetPitchSpanIntervalAndNotes with 1 parameter, do not use any kind of loop.pitchSpanFromC4
getPitchSpanIntervalAndNotes with 1 parameter, call pitchSpanFromC4 in at least one place.sortByInterval with 2 parameters
def to define sortByInterval with 2 parameterssort
sortByInterval with 2 parameters, call sort in at least one place.sortByInterval with 2 parameters, do not use any kind of loop.return statement
sortByInterval with 2 parameters, do not use return _.sortByDirection with 1 parameter
def to define sortByDirection with 1 parametersort
sortByDirection with 1 parameter, call sort in at least one place.sortByDirection with 1 parameter, do not use any kind of loop.return statement
sortByDirection with 1 parameter, do not use return _.sortBySpan with 1 parameter
def to define sortBySpan with 1 parametersort
sortBySpan with 1 parameter, call sort in at least one place.sortBySpan with 1 parameter, do not use any kind of loop.return statement
sortBySpan with 1 parameter, do not use return _.addMeasureSequence with 3 parameters
def to define addMeasureSequence with 3 parametersaddMeasureSequence with 3 parameters, use any kind of loop in at least one place.setPitch
addMeasureSequence with 3 parameters, call setPitch in exactly one place.setTime
addMeasureSequence with 3 parameters, call setTime in exactly one place.customMeasure
addMeasureSequence with 3 parameters, call customMeasure in at least one place.sort
addMeasureSequence with 3 parameters, do not call sort.return statement
addMeasureSequence with 3 parameters, do not use return _.