def dynamics(start, end=None):
    """
    Apply dynamics to a sequence. If end is specified, it will crescendo or diminuendo linearly from start to end dynamics.

    You can pass any of these strings as dynamic markers: ['pppppp', 'ppppp', 'pppp', 'ppp', 'pp', 'p', 'mp', 'mf', 'f', 'ff', 'fff', ''ffff]

    Args:
        start: beginning dynamic marker, if no end is specified all notes will get this marker
        end: ending dynamic marker, if unspecified the entire sequence will get the start dynamic marker

    Example usage:

        s1 | dynamics('p')  # play a sequence in piano
        s2 | dynamics('p', 'ff')  # crescendo from p to ff
        s3 | dynamics('ff', 'p')  # diminuendo from ff to p
    """
    def _(sequence):
        if start in _dynamic_markers_to_velocity:
            start_velocity = _dynamic_markers_to_velocity[start]
            start_marker = start
        else:
            raise ValueError("Unknown start dynamic: %s, must be in %s" % (start, _dynamic_markers_to_velocity.keys()))

        if end is None:
            end_velocity = start_velocity
            end_marker = start_marker
        elif end in _dynamic_markers_to_velocity:
            end_velocity = _dynamic_markers_to_velocity[end]
            end_marker = end
        else:
            raise ValueError("Unknown end dynamic: %s, must be in %s" % (start, _dynamic_markers_to_velocity.keys()))

        retval = sequence.__class__([Point(point) for point in sequence._elements])

        velocity_interval = (float(end_velocity) - float(start_velocity)) / (len(retval) - 1) if len(retval) > 1 else 0
        velocities = [int(start_velocity + velocity_interval * pos) for pos in range(len(retval))]

        # insert dynamics markers for lilypond
        if start_velocity > end_velocity:
            retval[0]["dynamic"] = "diminuendo"
            retval[-1]["dynamic"] = end_marker
        elif start_velocity < end_velocity:
            retval[0]["dynamic"] = "crescendo"
            retval[-1]["dynamic"] = end_marker
        else:
            retval[0]["dynamic"] = start_marker

        for point, velocity in zip(retval, velocities):
            point["velocity"] = velocity

        return retval
    return _
Apply dynamics to a sequence. If end is specified, it will crescendo or diminuendo linearly from start to end dynamics.

    You can pass any of these strings as dynamic markers: ['pppppp', 'ppppp', 'pppp', 'ppp', 'pp', 'p', 'mp', 'mf', 'f', 'ff', 'fff', ''ffff]

    Args:
        start: beginning dynamic marker, if no end is specified all notes will get this marker
        end: ending dynamic marker, if unspecified the entire sequence will get the start dynamic marker

    Example usage:

        s1 | dynamics('p')  # play a sequence in piano
        s2 | dynamics('p', 'ff')  # crescendo from p to ff
        s3 | dynamics('ff', 'p')  # diminuendo from ff to p