Exemplo n.º 1
0
    def parse_measure(self, measure, divisions):
        """
        Parses a measure according to the pitch duration token parsing process.
        :param measure: a measure dict in the format created by src/processing/conversion/xml_to_json.py
        :param divisions: the number of divisions a quarter note is split into in this song's MusicXML representation
        :return: a dict containing a list of groups containing the pitch numbers, durations, and bar positions for each
                harmony in a measure
        """
        parsed_measure = {"groups": []}

        tick_idx = 0
        for group in measure["groups"]:
            parsed_group = {
                "harmony": {},
                "pitch_numbers": [],
                "duration_tags": [],
                "bar_positions": []
            }
            harmony = Harmony(group["harmony"])
            parsed_group["harmony"]["root"] = harmony.get_one_hot_root()
            parsed_group["harmony"][
                "pitch_classes"] = harmony.get_seventh_pitch_classes_binary()
            dur_ticks_list = []
            for note_dict in group["notes"]:
                # want monophonic, so we'll just take the top note
                if "chord" in note_dict.keys() or "grace" in note_dict.keys():
                    continue
                else:
                    pitch_num, dur_tag, dur_ticks = self.parse_note(
                        note_dict, divisions)
                    parsed_group["pitch_numbers"].append(pitch_num)
                    parsed_group["duration_tags"].append(dur_tag)
                    dur_ticks_list.append(dur_ticks)
            unnorm_barpos = [
                tick_idx + sum(dur_ticks_list[:i])
                for i in range(len(dur_ticks_list))
            ]
            bar_positions = [
                int((dur_ticks / (4 * divisions)) * 96)
                for dur_ticks in unnorm_barpos
            ]
            parsed_group["bar_positions"] = bar_positions
            parsed_measure["groups"].append(parsed_group)
            tick_idx += sum(dur_ticks_list)
        return parsed_measure
Exemplo n.º 2
0
    def parse_measure(self, measure, scale_factor, prev_harmony):
        """
        For a measure, returns a set of ticks grouped by associated harmony in.
        :param measure: a measure dict in the format created by src/processing/conversion/xml_to_json.py
        :param scale_factor: the scale factor between XML divisions and midi ticks
        :param prev_harmony: a reference to the last harmony used in case a measure has none
        :return: a dict containing a list of groups that contains a harmony and the midi ticks associated with that harmony
        """
        parsed_measure = {"groups": []}
        total_ticks = 0
        for group in measure["groups"]:
            # Set note value for each tick in the measure
            group_ticks = []
            for note in group["notes"]:
                if not "duration" in note:
                    print("Skipping grace note...")
                    continue
                divisions = int(note["duration"]["text"])
                num_ticks = int(scale_factor * divisions)
                index = self.get_note_index(note)

                for i in range(num_ticks):
                    tick = [0 for _ in range(MIDI_RANGE)]
                    tick[index] = 1
                    group_ticks.append(tick)

            total_ticks += len(group_ticks)

            if not group["harmony"]:
                parsed_measure["groups"].append({
                    "harmony": prev_harmony,
                    "ticks": group_ticks
                })
            else:
                harmony = Harmony(group["harmony"])
                harmony_dict = {
                    "root": harmony.get_one_hot_root(),
                    "pitch_classes":
                    harmony.get_seventh_pitch_classes_binary()
                }
                prev_harmony = harmony_dict
                parsed_measure["groups"].append({
                    "harmony": harmony_dict,
                    "ticks": group_ticks
                })

            # Mitigate ticks for chords that occur mid-note
            for i, group in enumerate(parsed_measure["groups"]):
                try:
                    if not group["ticks"]:
                        # Handle the case of no harmony at the start of the bar
                        if not 0 in measure["harmonies_start"]:
                            measure["harmonies_start"].insert(0, 0)

                        correct_len_of_prev_harmony = int(
                            scale_factor * (measure["harmonies_start"][i] -
                                            measure["harmonies_start"][i - 1]))

                        group["ticks"].extend(parsed_measure["groups"][
                            i - 1]["ticks"][correct_len_of_prev_harmony:])
                        parsed_measure["groups"][
                            i - 1]["ticks"] = parsed_measure["groups"][
                                i - 1]["ticks"][:correct_len_of_prev_harmony]
                except:
                    import pdb
                    pdb.set_trace()
                    raise (
                        "No ticks in the first group of a measure! (in fix for chords mid-note)"
                    )

        if total_ticks > TICKS_PER_BEAT * 4:
            raise Exception("OH NO BRO. YOUR TICKS ARE TOO MUCH YO")

        i = 0
        while total_ticks < TICKS_PER_BEAT * 4:
            group = parsed_measure["groups"][i]
            spacer_tick = [0 for _ in range(MIDI_RANGE)]
            spacer_tick[0] = 1  # fill with rests
            group["ticks"].append(spacer_tick)
            i = (i + 1) % len(parsed_measure["groups"])
            total_ticks += 1

        parsed_measure["num_ticks"] = total_ticks
        return parsed_measure, prev_harmony