def __init__(self,
              multiply: Union[Real, Envelope, Sequence] = 1,
              add: Union[Real, Envelope, Sequence] = 0):
     self.multiply = Envelope.from_list(multiply) if isinstance(
         multiply, Sequence) else multiply
     self.add_amount = Envelope.from_list(add) if isinstance(
         add, Sequence) else add
Exemple #2
0
    def _assert_curves_are_valid(minima_curve: expenvelope.Envelope,
                                 maxima_curve: expenvelope.Envelope):
        """Helper method that asserts the curves are a valid min/max pair."""

        # make sure both curves have equal duration
        try:
            assert minima_curve.end_time() == maxima_curve.end_time()
        except AssertionError:
            message = (
                "Found unequal duration when comparing 'minima_curve' (with end_time ="
                " '{}') and 'maxima_curve' (with end_time = '{}').".format(
                    minima_curve.end_time(), maxima_curve.end_time()))
            message += " Make sure both curves have equal duration."
            raise ValueError(message)

        # compare all local extrema to make sure that at any time
        # point minima_curve < maxima_curve
        points_to_compare = (
            minima_curve.local_extrema(include_saddle_points=True) +
            maxima_curve.local_extrema(include_saddle_points=True) +
            [0, minima_curve.end_time()])
        for time in points_to_compare:
            try:
                assert minima_curve.value_at(time) < maxima_curve.value_at(
                    time)
            except AssertionError:
                message = (
                    "At time '{}' 'minima_curve' isn't smaller than 'maxima_curve'!"
                    .format(time))
                raise ValueError(message)
Exemple #3
0
 def _convert_params_to_envelopes_if_needed(self):
     # if we've been given extra parameters of playback, and their values are envelopes written as lists, etc.
     # this converts them all to Envelope objects
     for param, value in self.iterate_extra_parameters_and_values():
         if hasattr(value, '__len__'):
             self["param_" + param] = Envelope.from_list(value)
             self.temp["parameters_that_came_from_lists"].add(param)
Exemple #4
0
    def register_note(self, instrument: ScampInstrument,
                      note_info: dict) -> None:
        """
        Called when an instrument wants to register that it finished a note, records note in all transcriptions

        :param instrument: the ScampInstrument that played the note
        :param note_info: the note info dictionary on that note, containing time stamps, parameter changes, etc.
        """
        assert note_info[
            "end_time_stamp"] is not None, "Cannot register unfinished note!"
        param_change_segments = note_info["parameter_change_segments"]

        if note_info["start_time_stamp"].time_in_master == note_info[
                "end_time_stamp"].time_in_master:
            return

        # loop through all the transcriptions in progress
        for performance, clock, clock_start_beat, units in self._transcriptions_in_progress:
            # figure out the start_beat and length relative to this transcription's clock and start beat
            start_beat_in_clock = Transcriber._resolve_time_stamp(
                note_info["start_time_stamp"], clock, units)
            end_beat_in_clock = Transcriber._resolve_time_stamp(
                note_info["end_time_stamp"], clock, units)

            note_start_beat = start_beat_in_clock - clock_start_beat
            note_length = end_beat_in_clock - start_beat_in_clock

            # handle split points (if applicable) by creating a note length sections tuple
            note_length_sections = None
            if len(note_info["split_points"]) > 0:
                note_length_sections = []
                last_split = note_start_beat
                for split_point in note_info["split_points"]:
                    split_point_beat = Transcriber._resolve_time_stamp(
                        split_point, clock, units)
                    note_length_sections.append(split_point_beat - last_split)
                    last_split = split_point_beat
                if end_beat_in_clock > last_split:
                    note_length_sections.append(end_beat_in_clock - last_split)
                note_length_sections = tuple(note_length_sections)

            # get curves for all the parameters
            extra_parameters = {}
            for param in note_info["parameter_start_values"]:
                if param in param_change_segments and len(
                        param_change_segments[param]) > 0:
                    levels = [note_info["parameter_start_values"][param]]
                    # keep track of this in case of gaps between segments
                    beat_of_last_level_recorded = start_beat_in_clock
                    durations = []
                    curve_shapes = []
                    for param_change_segment in param_change_segments[param]:
                        # no need to transcribe a param_change_segment that was aborted immediately
                        if param_change_segment.duration == 0 and \
                                param_change_segment.end_level == param_change_segment.start_level:
                            continue

                        param_start_beat_in_clock = Transcriber._resolve_time_stamp(
                            param_change_segment.start_time_stamp, clock,
                            units)
                        param_end_beat_in_clock = Transcriber._resolve_time_stamp(
                            param_change_segment.end_time_stamp, clock, units)

                        # if there's a gap between the last level we recorded and this segment, we need to fill it with
                        # a flat segment that holds the last level recorded
                        if param_start_beat_in_clock > beat_of_last_level_recorded:
                            durations.append(param_start_beat_in_clock -
                                             beat_of_last_level_recorded)
                            levels.append(levels[-1])
                            curve_shapes.append(0)

                        durations.append(param_end_beat_in_clock -
                                         param_start_beat_in_clock)
                        levels.append(param_change_segment.end_level)
                        curve_shapes.append(param_change_segment.curve_shape)

                        beat_of_last_level_recorded = param_end_beat_in_clock

                    # again, if we end the curve early, then we need to add a flat filler segment
                    if beat_of_last_level_recorded < note_start_beat + note_length:
                        durations.append(note_start_beat + note_length -
                                         beat_of_last_level_recorded)
                        levels.append(levels[-1])
                        curve_shapes.append(0)

                    # assign to specific variables for pitch and volume, otherwise put in a dictionary of extra params
                    if param == "pitch":
                        # note that if the length of levels is 1, then there's been no meaningful animation
                        # so just act like it's not animated. This probably shouldn't really come up. (It was
                        # coming up before with zero-length notes, but now those are just skipped anyway.)
                        if len(levels) == 1:
                            pitch = levels[0]
                        else:
                            pitch = Envelope.from_levels_and_durations(
                                levels, durations, curve_shapes)
                    elif param == "volume":
                        if len(levels) == 1:
                            volume = levels[0]
                        else:
                            volume = Envelope.from_levels_and_durations(
                                levels, durations, curve_shapes)
                    else:
                        if len(levels) == 1:
                            extra_parameters[param] = levels[0]
                        else:
                            extra_parameters[
                                param] = Envelope.from_levels_and_durations(
                                    levels, durations, curve_shapes)
                else:
                    # assign to specific variables for pitch and volume, otherwise put in a dictionary of extra params
                    if param == "pitch":
                        pitch = note_info["parameter_start_values"]["pitch"]
                    elif param == "volume":
                        volume = note_info["parameter_start_values"]["volume"]
                    else:
                        extra_parameters[param] = note_info[
                            "parameter_start_values"][param]

            for instrument_part in performance.get_parts_by_instrument(
                    instrument):
                # it'd be kind of weird for more than one part to have the same instrument, but if they did,
                # I suppose that each part should transcribe the note
                instrument_part.new_note(
                    note_start_beat, note_length_sections
                    if note_length_sections is not None else note_length,
                    pitch, volume, note_info["properties"])
Exemple #5
0
#  3 of the License, or (at your option) any later version.                                      #
#                                                                                                #
#  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;     #
#  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     #
#  See the GNU General Public License for more details.                                          #
#                                                                                                #
#  You should have received a copy of the GNU General Public License along with this program.    #
#  If not, see <http://www.gnu.org/licenses/>.                                                   #
#  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  #

from expenvelope import Envelope
import math

# Ways of constructing Envelopes

envelope_from_levels_and_durations = Envelope((1.0, 0.2, 0.6, 0),
                                              (2.0, 1.0, 3.0))
# alternative: `Envelope.from_levels_and_durations((1.0, 0.2, 0.6, 0), (2.0, 1.0, 3.0))`
envelope_from_levels_and_durations.show_plot(
    "Envelope from levels and durations")

envelope_from_levels_and_durations_with_curve_shapes = Envelope(
    (1.0, 0.2, 0.6, 0), (2.0, 1.0, 3.0), curve_shapes=(2, 2, -3))
# alternative: `Envelope.from_levels_and_durations((1.0, 0.2, 0.6, 0), (2.0, 1.0, 3.0), curve_shapes=(2, 2, -3))`
envelope_from_levels_and_durations_with_curve_shapes.show_plot(
    "Envelope with curve shapes")

envelope_from_levels = Envelope.from_levels((1.0, 0.2, 0.6, 0), length=3)
envelope_from_levels.show_plot("Envelope from levels and total length")

envelope_from_points = Envelope.from_points((-1, 5), (1, 6), (5, -2))
envelope_from_points.show_plot("Envelope from points")
Exemple #6
0
 def _convert_params_to_envelopes_if_needed(self):
     # if we've been given extra parameters of playback, and their values are envelopes written as lists, etc.
     # this converts them all to Envelope objects
     for key, value in self.items():
         if (key.startswith("param_") or key.endswith("_param")) and isinstance(value, (list, tuple)):
             self[key] = Envelope.from_list(value)