docs: 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 _