コード例 #1
0
ファイル: solve.py プロジェクト: rmwu/harmony-solver
def get_path(filename):
	"""
	get_path
		takes in a filename, loads the data, and returns the
		path found by Harmony, or None

	Parameters
		data: dictionary mapping n, colors, and swaps for 
			a given initial state of the game
	"""
	data = get_harmony_text(filename)

	# get values from data dict
	n = data["n"]
	colors = data["colors"]
	swaps = data["swaps"]
	test = data["test"]
	# this formatting is if filename = "cases/#.in"
	print "Test {}: {}\n".format(filename[6], test)

	# load and solve game
	harmony = Harmony(n, colors, swaps)
	path = harmony.solve()

	return path
コード例 #2
0
    def testLogic_game_solved(self):
        """
		testLogic_game_solved
			tests whether a solved game is reported as solved
		"""
        n = 2
        colors = [0, 0, 1, 1]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)

        self.assertTrue(harmony.game_solved())
コード例 #3
0
    def testSearch_none(self):
        """
		testSearch_none
			tests if None is returned upon an impossible situation
		"""
        n = 2
        colors = [1, 1, 0, 0]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)
        path = harmony.solve()

        assert path is None
コード例 #4
0
    def testPathfinding_get_swappable_none(self):
        """
		testPathfinding_get_swappable_none
			tests that get_swappable returns an empty list
			given no swaps available
		"""
        n = 2
        colors = [0, 0, 1, 1]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)

        self.assertEqual(harmony.get_swappable(), [])
コード例 #5
0
    def testLogic_has_waps_left_none(self):
        """
		testLogic_has_waps_left_none
			tests whether swaps_left, given no remaining total
			swaps, returns False
		"""
        n = 2
        colors = [0, 0, 1, 1]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)

        self.assertFalse(harmony.has_swaps_left())
コード例 #6
0
    def __init__(self, *args, **kwargs):
        """
		constructor
			initializes and stores a 2 by 2 game of
			Harmony for use in tests
		"""
        super(TestHarmonySmall, self).__init__(*args, **kwargs)

        self.n = 2
        colors = [0, 1, 1, 0]
        swaps = [0, 1, 0, 1]
        self.harmony = Harmony(self.n, colors, swaps)
コード例 #7
0
    def testSearch_complete(self):
        """
		testSearch_complete
			tests if an empty array is returned upon an already
			solved game
		"""
        n = 2
        colors = [0, 0, 1, 1]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)
        path = harmony.solve()

        self.assertEqual(path, [])
コード例 #8
0
    def testPathfinding_valid_moves_many(self):
        """
		testPathfinding_valid_moves_many
			verifies that the valid_moves locates all valid
			moves, many in this case
		"""
        n = 2
        colors = [1, 1, 0, 0]
        swaps = [1, 1, 1, 1]
        harmony = Harmony(n, colors, swaps)

        index = 0
        valid = [2]

        self.assertEqual(valid, harmony.valid_moves(index))
コード例 #9
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
コード例 #10
0
ファイル: form.py プロジェクト: jonathanmarmor/animalplay
    def __init__(self, score):
        print "making score"
        self.score = score
        self.bars = []

        print "choosing drones"
        self.drones = choose_drones()

        print "initializing harmony options"
        self.harmony = Harmony(self.drones)

        print "initializing harmonic rhythm options"
        self.harmonic_rhythm_factory = harmonic_rhythm.HarmonicRhythm()

        print "making configs for each bar"
        self.make_bars()
        print "making section config groupings"
        self.group_sections()
        print "creating staves"
        self.set_staves()

        print "adjusting soloist entrances"
        self.adjust_soloist_entrances()

        print "filling some staves with rests"
        self.temp_fill_with_rests()

        print "making harmonic rhythm"
        self.make_harmonic_rhythm()
        print "making drones"
        self.make_drones()
        print "choosing harmonies"
        self.choose_harmonies()

        print "making bassline"
        self.make_bassline()
        print "making soloist"
        self.make_soloist()
        print "making accompaniment"
        self.make_accompaniment()
        print "making piano right hand"
        self.make_piano_right_hand()

        print "adding rehearsal marks"
        self.add_rehearsal_marks()
        print "adding dynamics"
        self.add_dynamics()
        print "adding double barlines"
        self.add_double_barlines()

        print "adding final barline"
        self.add_final_barlines()

        print "Done!"
コード例 #11
0
    def testConstructor_swapping_points_none(self):
        """
		testConstructor_swapping_points_none
			tests that Harmony extracts the correct
			starting points, given that there are none
		"""
        n = 2
        colors = [0, 1, 1, 0]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)

        swapping_points = set()

        self.assertEqual(swapping_points, set(harmony.swapping_points))
コード例 #12
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
コード例 #13
0
class TestHarmonySmall(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        """
		constructor
			initializes and stores a 2 by 2 game of
			Harmony for use in tests
		"""
        super(TestHarmonySmall, self).__init__(*args, **kwargs)

        self.n = 2
        colors = [0, 1, 1, 0]
        swaps = [0, 1, 0, 1]
        self.harmony = Harmony(self.n, colors, swaps)

    ################################
    # Testing Harmony constructor
    ################################
    def testConstructor_n(self):
        """
		testConstructor_n
			tests whether Harmony creates an object
			knowing its correct size
		"""
        self.assertEqual(self.n, self.harmony.n)

    def testConstructor_grid_size(self):
        """
		testConstructor_grid_size
			tests whether Harmony initializes colors
			and swaps of the right size
		"""
        size = self.n**2
        self.assertEqual(size, len(self.harmony.colors))
        self.assertEqual(size, len(self.harmony.swaps))

    def testConstructor_swaps_left(self):
        """
		testConstructor_swaps_left
			tests whether Harmony records the correct
			number of swaps available upon creation
		"""
        self.assertEqual(2, self.harmony.swaps_left)

    def testConstructor_swapping_points_none(self):
        """
		testConstructor_swapping_points_none
			tests that Harmony extracts the correct
			starting points, given that there are none
		"""
        n = 2
        colors = [0, 1, 1, 0]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)

        swapping_points = set()

        self.assertEqual(swapping_points, set(harmony.swapping_points))

    def testConstructor_swapping_points_exist(self):
        """
		testConstructor_swapping_points_exist
			tests that Harmony extracts the correct
			starting points, given that they exist
		"""
        swapping_points = set([1, 3])

        self.assertEqual(swapping_points, set(self.harmony.swapping_points))

    ################################
    # Testing index manipulations
    ################################
    def testIndexManip_list_to_grid_index_1(self):
        """
		testIndexManip_list_to_grid_index_1
			tests method list_to_grid_index to ensure
			that indices are being property translated
			from list to grid, for the corner of the grid.
		"""
        index = 3
        grid_coords = (1, 1)

        self.assertEqual(grid_coords, self.harmony.list_to_grid_index(index))

    def testIndexManip_list_to_grid_index_2(self):
        """
		testIndexManip_list_to_grid_index_2
			tests method list_to_grid_index to ensure
			that indices are being property translated
			from list to grid, for an intermediate element
			in the grid
		"""
        index = 2
        grid_coords = (1, 0)

        self.assertEqual(grid_coords, self.harmony.list_to_grid_index(index))

    def testIndexManip_grid_to_list_index_1(self):
        """
		testIndexManip_grid_to_list_index_1
			tests method grid_to_list_index to ensure
			that indices are being property translated
			from grid to list, for the corner of the grid
		"""
        index = 3
        g = (1, 1)

        self.assertEqual(index, self.harmony.grid_to_list_index(g))

    def testIndexManip_grid_to_list_index_2(self):
        """
		testIndexManip_grid_to_list_index_2
			tests method grid_to_list_index to ensure
			that indices are being property translated
			from grid to list, for an intermediate element
			in the grid
		"""
        index = 2
        g = (1, 0)

        self.assertEqual(index, self.harmony.grid_to_list_index(g))

    def testIndexManip_valid_index_origin(self):
        """
		testIndexManip_valid_index_origin
			tests method valid_index, given the origin
		"""
        index = 0

        self.assertTrue(self.harmony.valid_index(index))

    def testIndexManip_valid_index_arbitrary(self):
        """
		testIndexManip_valid_index_arbitrary
			tests method valid_index, given an arbitrary,
			non-boundary, valid index
		"""
        index = randint(1, 2)

        self.assertTrue(self.harmony.valid_index(index))

    def testIndexManip_valid_index_last(self):
        """
		testIndexManip_valid_index_last
			tests method valid_index, given the last valid
			index
		"""
        index = 3

        self.assertTrue(self.harmony.valid_index(index))

    def testIndexManip_invalid_index_before(self):
        """
		testIndexManip_invalid_index_before
			tests method valid_index, given a negative, invalid
			index
		"""
        index = -1

        self.assertFalse(self.harmony.valid_index(index))

    def testIndexManip_invalid_index_after(self):
        """
		testIndexManip_invalid_index_before
			tests method valid_index, given a negative, invalid
			index
		"""
        index = 5

        self.assertFalse(self.harmony.valid_index(index))

    def testIndexManip_indices_in_line_horizontal(self):
        """
		testIndexManip_indices_in_line_horizontal
			tests whether two horizontal colinear indices are
			reported as in line with each other
		"""
        index1 = 0
        index2 = 1

        self.assertTrue(self.harmony.indices_in_line(index1, index2))

    def testIndexManip_indices_in_line_vertical(self):
        """
		testIndexManip_indices_in_line_vertical tests
			whether two vertical colinear indices are
			reported as in line with each other
		"""
        index1 = 0
        index2 = 2

        self.assertTrue(self.harmony.indices_in_line(index1, index2))

    def testIndexManip_indices_in_line_nonlinear(self):
        """
		testIndexManip_indices_in_line_nonlinear
			tests whether two non-linear indices are
			reported as in not line with each other
		"""
        index1 = 0
        index2 = 3

        self.assertFalse(self.harmony.indices_in_line(index1, index2))

    ################################
    # Testing basic getter / setter
    ################################
    def testBasic_get(self):
        """
		testBasic_get
			tests whether Harmony can retrieve correct
			values, given a list index
		"""
        index = 3
        value = (0, 1)

        self.assertEqual(value, self.harmony.get(index))

    def testBasic_set_value(self):
        """
		testBasic_get
			tests whether Harmony can set correct values
			given a list index and value
		"""
        index = 3
        old_color, old_swaps = self.harmony.get(index)
        color, swaps = (1, 1)

        # should not be equal before setting
        self.assertNotEqual((color, swaps), self.harmony.get(index))

        self.harmony.set_value(index, color, swaps)

        # should be equal after setting
        self.assertEqual((color, swaps), self.harmony.get(index))

        # restore the original grid
        self.harmony.set_value(index, old_color, old_swaps)

    ################################
    # Testing game logic
    ################################
    def testLogic_valid_swap(self):
        """
		testLogic_valid_swap
			tests whether a valid swap is reported as valid
		"""
        index1 = 1
        index2 = 3

        self.assertTrue(self.harmony.valid_swap(index1, index2))

    def testLogic_invalid_swap(self):
        """
		testLogic_invalid_swap
			tests whether a invalid swap is reported as 
			not valid
		"""
        index1 = 0
        index2 = 2

        self.assertFalse(self.harmony.valid_swap(index1, index2))

    def testLogic_has_swaps_left_true(self):
        """
		testLogic_has_swaps_left_true
			tests whether swaps_left, given a positive number of
			remaining total swaps, returns True
		"""
        self.assertTrue(self.harmony.has_swaps_left())

    def testLogic_has_waps_left_none(self):
        """
		testLogic_has_waps_left_none
			tests whether swaps_left, given no remaining total
			swaps, returns False
		"""
        n = 2
        colors = [0, 0, 1, 1]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)

        self.assertFalse(harmony.has_swaps_left())

    def testLogic_game_unsolved(self):
        """
		testLogic_game_unsolved
			tests whether an unsolved game is reported as unsolved
		"""
        self.assertFalse(self.harmony.game_solved())

    def testLogic_game_solved(self):
        """
		testLogic_game_solved
			tests whether a solved game is reported as solved
		"""
        n = 2
        colors = [0, 0, 1, 1]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)

        self.assertTrue(harmony.game_solved())

    ################################
    # Testing pathfinding helpers
    ################################
    def testPathfinding_swap(self):
        """
		testPathfinding_swap
			tests whether swap correctly swaps two colors,
			decreases the swaps available for each, and decreases
			the total number of swaps left
		"""
        index1 = 1
        index2 = 3

        color1, swaps1 = self.harmony.get(index1)
        color2, swaps2 = self.harmony.get(index2)
        old_total = self.harmony.swaps_left

        # swap here, and then observe new color / swaps
        self.harmony.swap(index1, index2)

        color1_new, swaps1_new = self.harmony.get(index1)
        color2_new, swaps2_new = self.harmony.get(index2)
        new_total = self.harmony.swaps_left

        # check color swapping
        self.assertEqual(color1, color2_new)
        self.assertEqual(color2, color1_new)

        # check swaps decrease
        self.assertEqual(swaps1_new, swaps1 - 1)
        self.assertEqual(swaps2_new, swaps2 - 1)

        # check total swaps decrease
        self.assertEqual(new_total, old_total - 2)

    def testPathfinding_unswap(self):
        """
		testPathfinding_unswap
			tests whether unswap correctly reverts two colors,
			re-increases the swaps available, and re-increases
			the total number of swaps left
		"""
        index1 = 1
        index2 = 3

        color1, swaps1 = self.harmony.get(index1)
        color2, swaps2 = self.harmony.get(index2)
        old_total = self.harmony.swaps_left

        # swap here, and then observe new color / swaps
        self.harmony.unswap(index1, index2)

        color1_new, swaps1_new = self.harmony.get(index1)
        color2_new, swaps2_new = self.harmony.get(index2)
        new_total = self.harmony.swaps_left

        # check color swapping
        self.assertEqual(color1, color2_new)
        self.assertEqual(color2, color1_new)

        # check swaps decrease
        self.assertEqual(swaps1_new, swaps1 + 1)
        self.assertEqual(swaps2_new, swaps2 + 1)

        # check total swaps decrease
        self.assertEqual(new_total, old_total + 2)

    def testPathfinding_valid_moves_none(self):
        """
		testPathfinding_valid_moves_none
			verifies that the valid_moves locates no valid
			moves if there are none
		"""
        index = 0

        self.assertEqual([], self.harmony.valid_moves(index))

    def testPathfinding_valid_moves_one(self):
        """
		testPathfinding_valid_moves_one
			verifies that the valid_moves locates all valid
			moves, one in this case
		"""
        index = 1

        self.assertEqual([3], self.harmony.valid_moves(index))

    def testPathfinding_valid_moves_many(self):
        """
		testPathfinding_valid_moves_many
			verifies that the valid_moves locates all valid
			moves, many in this case
		"""
        n = 2
        colors = [1, 1, 0, 0]
        swaps = [1, 1, 1, 1]
        harmony = Harmony(n, colors, swaps)

        index = 0
        valid = [2]

        self.assertEqual(valid, harmony.valid_moves(index))

    def testPathfinding_get_swappable_none(self):
        """
		testPathfinding_get_swappable_none
			tests that get_swappable returns an empty list
			given no swaps available
		"""
        n = 2
        colors = [0, 0, 1, 1]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)

        self.assertEqual(harmony.get_swappable(), [])

    def testPathfinding_get_swappable_many(self):
        """
		testPathfinding_get_swappable_many
			tests that get_swappable returns a correct
			list of still swappable states, if there are
			some
		"""
        swappable = set([1, 3])

        self.assertEqual(swappable, set(self.harmony.get_swappable()))

    ################################
    # Testing search algorithm
    ################################
    def testSearch_none(self):
        """
		testSearch_none
			tests if None is returned upon an impossible situation
		"""
        n = 2
        colors = [1, 1, 0, 0]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)
        path = harmony.solve()

        assert path is None

    def testSearch_complete(self):
        """
		testSearch_complete
			tests if an empty array is returned upon an already
			solved game
		"""
        n = 2
        colors = [0, 0, 1, 1]
        swaps = [0, 0, 0, 0]
        harmony = Harmony(n, colors, swaps)
        path = harmony.solve()

        self.assertEqual(path, [])

    def testSearch_path_small(self):
        """
		testSearch_path_small
			tests if a valid path is found, given that it exists
			and the game does not start solved

			This path has length 1.
		"""
        path = self.harmony.solve()
        actual_length = 1

        self.assertEqual(len(path), actual_length)
コード例 #14
0
ファイル: form.py プロジェクト: jonathanmarmor/animalplay
class Form(object):
    def __init__(self, score):
        print "making score"
        self.score = score
        self.bars = []

        print "choosing drones"
        self.drones = choose_drones()

        print "initializing harmony options"
        self.harmony = Harmony(self.drones)

        print "initializing harmonic rhythm options"
        self.harmonic_rhythm_factory = harmonic_rhythm.HarmonicRhythm()

        print "making configs for each bar"
        self.make_bars()
        print "making section config groupings"
        self.group_sections()
        print "creating staves"
        self.set_staves()

        print "adjusting soloist entrances"
        self.adjust_soloist_entrances()

        print "filling some staves with rests"
        self.temp_fill_with_rests()

        print "making harmonic rhythm"
        self.make_harmonic_rhythm()
        print "making drones"
        self.make_drones()
        print "choosing harmonies"
        self.choose_harmonies()

        print "making bassline"
        self.make_bassline()
        print "making soloist"
        self.make_soloist()
        print "making accompaniment"
        self.make_accompaniment()
        print "making piano right hand"
        self.make_piano_right_hand()

        print "adding rehearsal marks"
        self.add_rehearsal_marks()
        print "adding dynamics"
        self.add_dynamics()
        print "adding double barlines"
        self.add_double_barlines()

        print "adding final barline"
        self.add_final_barlines()

        print "Done!"

    def make_bars(self):
        bar_index = 0
        for movement_number in Conf.movements:
            for drone in [True, None]:
                if drone:
                    drone = self.drones[movement_number]
                for volume in ["q1", "l1", "q2", "l2"]:
                    n_bars = weighted_choice(Conf.n_bars_options[movement_number], Conf.n_bars_weights[movement_number])
                    for bar_n in range(n_bars):
                        bar = dict(
                            movement_number=movement_number,
                            movement_name=Conf.movement_names[movement_number],
                            drone=drone,
                            volume=volume,
                            bar_index=bar_index,
                            soloist=Conf.soloist[movement_number],
                            accompanists=Conf.accompanists[movement_number] if volume.startswith("l") else (),
                            dynamics=Conf.dynamics[movement_number][volume[0]],
                        )
                        bar["dynamics"]["drone"] = Conf.dynamics[movement_number]["drone"]
                        self.bars.append(bar)
                        bar_index += 1

    def group_section(self, bars, attr):
        """Group a list of bar configs by a config attribute"""
        return [list(group) for key, group in groupby(bars, lambda x: x[attr])]

    def group_sections(self):
        self.movement_sections = self.group_section(self.bars, "movement_number")
        self.drone_sections = self.group_section(self.bars, "drone")
        self.volume_sections = self.group_section(self.bars, "volume")

        self.movement_volume_sections = [self.group_section(m, "volume") for m in self.movement_sections]

        self.harmonic_rhythm = []
        self.raw_harmonic_rhythm = []
        self.harmonic_rhythm_drones = []
        self.harmonies = []
        self.unused_harmonies = []

    def set_staves(self):
        self.staves = []
        for inst in self.score:
            if inst.name == "Piano":
                for staff in inst:
                    self.staves.append(staff)
            else:
                self.staves.append(inst)

    def add_rehearsal_marks(self):
        for section in self.volume_sections[4::4]:
            first_bar_index = section[0]["bar_index"]
            for staff in self.score:
                if staff.name == "Piano":
                    add_rehearsal_mark(staff[0][first_bar_index])
                else:
                    add_rehearsal_mark(staff[first_bar_index])

    def add_piano_dynamics(self):
        piano = self.score["Piano"][1]
        for section in self.volume_sections:
            first_bar_conf = section[0]
            bar_index = first_bar_conf["bar_index"]

            piano_first_note = piano[bar_index][0]
            add_dynamic(piano_first_note, first_bar_conf["dynamics"]["piano"])

    def add_soloist_dynamics(self):
        dynamic = None
        previous_soloist = None
        for bar in self.bars:
            i = bar["bar_index"]
            soloist_name = bar["soloist"]
            if soloist_name:
                dyn = bar["dynamics"]["soloist"]
                if dyn != dynamic or soloist_name != previous_soloist:
                    dynamic = dyn
                    previous_soloist = soloist_name
                    soloist = self.score[soloist_name]
                    first_note = soloist[i][0]
                    add_dynamic(first_note, dynamic)

    def add_accompanists_dynamics(self):
        for section in self.volume_sections:
            first_bar_conf = section[0]
            bar_index = first_bar_conf["bar_index"]
            for acc_name in first_bar_conf["accompanists"]:
                acc = self.score[acc_name]
                first_note = acc[bar_index][0]
                add_dynamic(first_note, first_bar_conf["dynamics"]["accompanists"])

    def add_synth_dynamics(self):
        synth = self.score["Synthesizer"]
        # TODO use the section where the drone is actually sounding, not the square section
        for section in self.drone_sections:
            first_bar_conf = section[0]
            bar_index = first_bar_conf["bar_index"]
            synth_first_note = synth[bar_index][0]
            if not isinstance(synth_first_note, Rest):
                add_dynamic(synth_first_note, first_bar_conf["dynamics"]["drone"])

    def add_dynamics(self):
        self.add_piano_dynamics()
        self.add_soloist_dynamics()
        self.add_accompanists_dynamics()
        self.add_synth_dynamics()

    def add_double_barlines(self):
        for section in self.volume_sections[:-1]:
            last_bar_conf = section[-1]
            bar_index = last_bar_conf["bar_index"]
            for staff in self.staves:
                add_double_barline(staff[bar_index][-1])

    def add_final_barlines(self):
        for staff in self.staves:
            add_final_barline(staff)

    def temp_fill_with_rests(self):
        no = ["Synthesizer", "Piano lower", "Piano upper"]
        staves = [s for s in self.staves if s.name not in no]
        for staff in staves:

            for bar in self.bars:
                staff.append(get_rest_bar())

    def make_harmonic_rhythm(self):
        for phrase in self.volume_sections:
            raw, bars = self.harmonic_rhythm_factory.choose(phrase)
            assert len(bars) == len(phrase)
            self.raw_harmonic_rhythm.append(raw)
            self.harmonic_rhythm.append(bars)

    def choose_harmonies(self):
        for drone, rhythm in zip(self.harmonic_rhythm_drones, self.raw_harmonic_rhythm):
            pitches = [self.harmony.choose(d) for d in drone]
            self.harmonies.append(pitches)
            self.unused_harmonies.append([p[:] for p in pitches[:]])

    def make_piano_right_hand(self):
        piano_upper = self.score["Piano"][0]
        previous = [0, 4, 7, 10]
        for harmonies, unused, rhythm in zip(self.harmonies, self.unused_harmonies, self.raw_harmonic_rhythm):
            chords = []
            for h, unused_h in zip(harmonies, unused):
                chord = next_piano_right_hand_chord(previous, h, unused_h)
                previous = chord[:]
                chords.append(chord)
                # [unused_h.remove(pitch % 12) for pitch in chord]

            bars = parse_rhythm(rhythm, chords)
            piano_upper.extend(bars)

    def make_bassline(self):
        previous = -8
        piano_lower = self.score["Piano"][1]
        for harmonies, unused, rhythm in zip(self.harmonies, self.unused_harmonies, self.raw_harmonic_rhythm):
            pitches = []
            for h, unused_h in zip(harmonies, unused):
                p = next_piano_bass_note(previous, h)
                previous = p
                pitches.append(p)

                unused_h.remove(p % 12)

            bars = parse_rhythm(rhythm, pitches)
            piano_lower.extend(bars)

    def adjust_soloist_entrances(self):
        movements = self.movement_sections
        for i, movement in enumerate(movements):
            next_i = (i + 1) % len(movements)
            next_soloist = movements[next_i][0]["soloist"]

            for bar in movements[next_i]:
                if bar["accompanists"] != ():
                    next_accompanists = bar["accompanists"]
                    break

            last_section = self.movement_volume_sections[i][-1]
            for bar_config in last_section:
                bar_config["accompanists"] = next_accompanists

            # Pick soloist exit and new soloist entrance

            # No one should ever write code like this
            last_sections = self.movement_volume_sections[i][-3:]
            last_sections[0] = last_sections[0][len(last_sections[0]) / 2 :]
            last_sections[-1] = last_sections[-1][: len(last_sections[-1]) / 2]

            lengths = [len(section) for section in last_sections]

            flat = []
            for section in last_sections:
                for bar in section:
                    flat.append(bar)

            exit_bar_index = random.choice(range(sum(lengths[:2])))

            for bar in flat[exit_bar_index:]:
                bar["soloist"] = None

            for bar in last_section[len(last_section) / 2 :]:
                bar["soloist"] = None

            possible_entrance_bars = flat[exit_bar_index + 1 :]
            entrance_bar_index = random.choice(range(len(possible_entrance_bars)))
            new_solist_bars = possible_entrance_bars[entrance_bar_index:]
            for bar in new_solist_bars:
                bar["soloist"] = next_soloist

            for bar in last_section[len(last_section) / 2 :]:
                bar["soloist"] = next_soloist

    def make_accompaniment(self):
        previous_a = None
        previous_b = None

        for harmonies, unused, rhythm, section_configs in zip(
            self.harmonies, self.unused_harmonies, self.raw_harmonic_rhythm, self.volume_sections
        ):
            bar_config = section_configs[0]
            bar_index = bar_config["bar_index"]
            accompanists = bar_config["accompanists"]
            if accompanists:

                rhythm, harmonies, unused = add_accompaniment_notes(rhythm, harmonies, unused)

                a_name, b_name = accompanists
                a = self.score[a_name]
                b = self.score[b_name]

                pitches_a = []
                pitches_b = []
                for h, unused_h in zip(harmonies, unused):
                    pitch_a, pitch_b = next_accompaniment_notes(
                        a_name, b_name, previous_a, previous_b, h, unused_h, bar_config["movement_number"]
                    )
                    previous_a, previous_b = pitch_a, pitch_b
                    pitches_a.append(pitch_a)
                    pitches_b.append(pitch_b)
                bars_a = parse_rhythm(rhythm, pitches_a)
                bars_b = parse_rhythm(rhythm, pitches_b)

                a[bar_index : bar_index + len(bars_a)] = bars_a
                b[bar_index : bar_index + len(bars_b)] = bars_b
            else:
                previous_a = None
                previous_b = None

    def make_soloist(self):
        soloists = [sec[0]["soloist"] for sec in self.volume_sections]
        # Is the soloist entering, playing through, exiting, or resting?
        actions = solo.get_actions(soloists)

        previous = None
        previous_soloist_name = None

        for i, section_configs in enumerate(self.volume_sections):
            print "\tVolume Section #{}".format(i)

            soloist_name = soloists[i]
            if soloist_name != previous_soloist_name:
                previous = None
                previous_soloist_name = soloist_name
            action = actions[i]

            bar_index = section_configs[0]["bar_index"]

            movement_number = section_configs[0]["movement_number"]

            if soloist_name:
                soloist = self.score[soloist_name]
                harmonies = self.harmonies[i]
                unused = self.unused_harmonies[i]
                rhythm = self.raw_harmonic_rhythm[i]

                rhythm, harmonies, unused = solo.add_notes(rhythm, harmonies, unused, soloist_name, movement_number)

                len_rhythm = len(rhythm)

                rests = []
                if action == "enter":
                    enter_index = 0
                    if len_rhythm == 2:
                        enter_index = 1
                    if len_rhythm > 2:
                        opts = range(1, len_rhythm - 1)
                        enter_index = random.choice(opts)
                    for _ in harmonies[:enter_index]:
                        rests.append("r")
                    harmonies, unused = harmonies[enter_index:], unused[enter_index:]
                if action == "exit":
                    exit_index = 0
                    if len_rhythm == 2:
                        exit_index = 1
                    if len_rhythm > 2:
                        opts = range(1, len_rhythm - 1)
                        exit_index = random.choice(opts)
                    for _ in harmonies[exit_index:]:
                        rests.append("r")
                    harmonies, unused = harmonies[:exit_index], unused[:exit_index]

                pitches = []
                for h, unused_h in zip(harmonies, unused):
                    pitch = solo.next_soloist_note(soloist_name, previous, h, movement_number, i)
                    if isinstance(pitch, int):
                        previous = pitch

                        pc = pitch % 12
                        if pc in unused_h:
                            unused_h.remove(pc)
                    elif isinstance(pitch, list):
                        previous = pitch[0]

                    pitches.append(pitch)

                if action == "enter":
                    pitches = rests + pitches

                elif action == "exit":
                    pitches = pitches + rests

                rhythm, pitches = solo.join_some_notes(rhythm, pitches)

                bars = parse_rhythm(rhythm, pitches)
                soloist[bar_index : bar_index + len(bars)] = bars

                if action == "enter" or action == "exit":
                    notes = []
                    for bar in bars:
                        for note in bar:
                            if not is_rest(note):
                                notes.append(note)
                    if action == "enter":
                        crescendo(notes)
                    elif action == "exit":
                        crescendo(notes, decrescendo=True)

            else:
                previous = None

    def make_drones(self):
        synth = self.score["Synthesizer"]

        # Drone 1
        bars = [get_one_note_bar(self.drones[0]) for _ in self.drone_sections[0]]
        tie(bars)
        synth.extend(bars)

        for rhythm in self.raw_harmonic_rhythm[:4]:
            self.harmonic_rhythm_drones.append([self.drones[0] for r in rhythm])

        # Rest 1
        rest_bars = [get_rest_bar() for _ in self.drone_sections[1]]
        synth.extend(rest_bars)

        for rhythm in self.raw_harmonic_rhythm[4:8]:
            self.harmonic_rhythm_drones.append([None for r in rhythm])

        # Drone 2
        bars = [get_one_note_bar(self.drones[1]) for _ in self.drone_sections[2]]
        tie(bars)
        synth.extend(bars)

        for rhythm in self.raw_harmonic_rhythm[8:12]:
            self.harmonic_rhythm_drones.append([self.drones[1] for r in rhythm])

        #######################################
        #### Rest 2 and Drone 3 A entrance ####
        #######################################

        # First two volume sections are resting
        rest_bars = []
        for section in self.volume_sections[12:14]:
            rest_bars.extend([get_rest_bar() for _ in section])

        for rhythm in self.raw_harmonic_rhythm[12:14]:
            self.harmonic_rhythm_drones.append([None for r in rhythm])

        # The drone comes in at a random point in the third volume section

        drone_3a = self.drones[2][0]

        entrance_section = self.raw_harmonic_rhythm[14]
        start = random.choice(range(len(entrance_section) - 1))

        rests = ["r" for duration in entrance_section[:start]]
        drones = [drone_3a for duration in entrance_section[start:]]

        pitches = rests + drones
        entrance_bars = parse_rhythm(entrance_section, pitches=pitches)

        rhythm = []
        for pitch in pitches:
            if pitch == "r":
                rhythm.append(None)
            else:
                rhythm.append(drone_3a)
        self.harmonic_rhythm_drones.append(rhythm)

        # Last volume section is droning
        drone_bars = [get_one_note_bar(drone_3a) for _ in self.volume_sections[15]]

        self.harmonic_rhythm_drones.append([drone_3a for dur in self.raw_harmonic_rhythm[15]])

        synth.extend(rest_bars + entrance_bars + drone_bars)

        to_tie = entrance_bars + drone_bars

        # TODO ties

        #######################
        #### Drone 3 A & B ####
        #######################

        # First volume section is drone 3 A
        bars_1 = [get_one_note_bar(drone_3a) for _ in self.volume_sections[16]]
        self.harmonic_rhythm_drones.append([drone_3a for dur in self.raw_harmonic_rhythm[16]])

        # Second volume section drone 3 B comes in
        entrance_section = self.raw_harmonic_rhythm[17]
        start = random.choice(range(1, len(entrance_section)))

        a = [drone_3a for duration in entrance_section[:start]]
        both = [self.drones[2] for duration in entrance_section[start:]]

        pitches = a + both
        bars_2 = parse_rhythm(entrance_section, pitches=pitches)

        self.harmonic_rhythm_drones.append(pitches)

        # Third and Fourth volume sections both drones
        bars_3_4 = []
        for section in self.volume_sections[18:20]:
            bars_3_4.extend([get_one_note_bar(self.drones[2]) for _ in section])

        for rhythm in self.raw_harmonic_rhythm[18:20]:
            self.harmonic_rhythm_drones.append([self.drones[2] for dur in rhythm])

        # Fifth volume section drone 3 A exits
        drone_3b = self.drones[2][1]

        exit_section = self.raw_harmonic_rhythm[20]
        start = random.choice(range(1, len(exit_section)))

        both = [self.drones[2] for duration in exit_section[:start]]
        b = [drone_3b for duration in exit_section[start:]]

        pitches = both + b
        bars_5 = parse_rhythm(exit_section, pitches=pitches)

        self.harmonic_rhythm_drones.append(pitches)

        # Sixth volume section is drone 3 B
        bars_6 = [get_one_note_bar(drone_3b) for _ in self.volume_sections[21]]
        self.harmonic_rhythm_drones.append([drone_3b for dur in self.raw_harmonic_rhythm[21]])

        # Seventh volume section drone 3 B exits
        exit_section = self.raw_harmonic_rhythm[22]
        start = random.choice(range(1, len(exit_section)))

        b = [drone_3b for duration in exit_section[:start]]
        rest = ["r" for duration in exit_section[start:]]

        pitches = b + rest
        bars_7 = parse_rhythm(exit_section, pitches=pitches)

        rhythm_drones = []
        for p in pitches:
            if p == "r":
                rhythm_drones.append(None)
            else:
                rhythm_drones.append(p)
        self.harmonic_rhythm_drones.append(rhythm_drones)

        # Eighth volume section is resting
        bars_8 = [get_rest_bar() for _ in self.volume_sections[23]]
        self.harmonic_rhythm_drones.append([None for dur in self.raw_harmonic_rhythm[23]])

        bars = bars_1 + bars_2 + bars_3_4 + bars_5 + bars_6 + bars_7 + bars_8
        synth.extend(bars)

        to_tie += bars
        tie(to_tie)

        # Drone 4
        bars = [get_one_note_bar(self.drones[3]) for _ in self.drone_sections[6]]
        tie(bars)
        synth.extend(bars)

        for rhythm in self.raw_harmonic_rhythm[24:28]:
            self.harmonic_rhythm_drones.append([self.drones[3] for r in rhythm])

        # Rest 4
        bars = [get_rest_bar() for _ in self.drone_sections[7]]
        synth.extend(bars)

        for rhythm in self.raw_harmonic_rhythm[28:]:
            self.harmonic_rhythm_drones.append([None for r in rhythm])