Skip to content

Commit

Permalink
Continued to clean code and fixed bug in new_note
Browse files Browse the repository at this point in the history
  • Loading branch information
Buddy28911 committed May 5, 2021
1 parent 6b17b0d commit e1bccf2
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 24 deletions.
18 changes: 8 additions & 10 deletions gen_alg.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
# Genetic Algorithm file
# gen_alg.py
# By Danny Noe
# Evolutionary Composition
# gen_alg.py contains the code for the genetic algorithm data and functions.

import random

import numpy

from deap import algorithms, base, creator, tools

from representation import representation, save_best_melodies, Melody


class algorithm_args:
"""
The algorithm_args object contains all of the algorithm arguments recieved on the command-line.
algorithm_args simplifies number of arguments that need to be sent to run_program()
"""
def __init__(self, algorithm: str, pop_size: int, ngen: int, mu: int, lambda_: int, cxpb: float, mutpb: float):
def __init__(self, algorithm: str, pop_size: int, ngen: int, mu: int, lambda_: int, cxpb: float, mutpb: float) -> None:
"""
Initializes the algorithm_args object with arguments given on the command-line
Input: algorithm: str, the name of the genetic algorithm | pop_size: int, the size of the initial population
Expand All @@ -33,7 +31,7 @@ def __init__(self, algorithm: str, pop_size: int, ngen: int, mu: int, lambda_: i
self.mutpb = mutpb
return

def cx_music(input_mel1: Melody, input_mel2: Melody):
def cx_music(input_mel1: Melody, input_mel2: Melody) -> tuple:
"""
cx_music() performs a crossover operation on two given melodies
Input: input_mel1: Melody, the first melody | input_mel2: Melody, the second melody
Expand All @@ -50,7 +48,7 @@ def cx_music(input_mel1: Melody, input_mel2: Melody):

return input_mel1, input_mel2

def mut_melody(input_mel: Melody):
def mut_melody(input_mel: Melody) -> tuple:
"""
mut_melody() mutates a given melody. The function iterates over the whole melody
performing a coin flip on each note determining if it should be pitch shifted up or down
Expand All @@ -68,7 +66,7 @@ def mut_melody(input_mel: Melody):

return input_mel,

def load_midi(population: list, toolbox, key: str):
def load_midi(population: list, toolbox, key: str) -> int:
"""
The load_midi() function reads MIDI files entered by the user from the ./midi_out/ directory
and adds them to the population
Expand Down Expand Up @@ -96,7 +94,7 @@ def load_midi(population: list, toolbox, key: str):

return len(population)

def run_genetic_algorithm(rep_obj, alg_args):
def run_genetic_algorithm(rep_obj, alg_args) -> tuple:
"""
The run_genetic_algorithm is the main method of the evolutionary composition project.
The method takes in all arguments, then runs the selected genetic algorithm.
Expand Down
6 changes: 4 additions & 2 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Main file for Danny Noe's Evolutionary Music Project
# main.py
# By Danny Noe
# Evolutionary Composition
# main.py is responsible for initializing the program, as well as handling the command-line arguments

import argparse
from representation import representation, available_outports
Expand All @@ -11,7 +14,6 @@
parser.add_argument('-k', '--key_signature', type=str, help="Sets the key signature for the program", choices=key_sig, default="C")
parser.add_argument('-t', '--tempo', type=int, help="Sets the tempo (in BPM) for the program", choices=range(1, 301), metavar="[0,300]", default=120)


def str2bool(v):
if isinstance(v, bool):
return v
Expand Down
1 change: 1 addition & 0 deletions music_data.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# music_date.py
# Danny Noe
# Evolutionary Composition
# music_data.py houses constant data used by representation.py. This file has no executable code

##############################
Expand Down
35 changes: 23 additions & 12 deletions representation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Representation.py
# representation.py
# By Danny Noe
# Evolutionary Composition
# representation.py is responsible for the musical representation data.

import random
import mido
Expand Down Expand Up @@ -368,9 +370,9 @@ def shift_scale(key: str) -> list:

return shifted_scale

def get_new_pitch(prev_pitch: int, interval: int, ascend_or_descend: int) -> int:
def calculate_pitch(prev_pitch: int, interval: int, ascend_or_descend: int) -> int:
"""
Helper function for get_new_note_pitch. Returns a new_pitch based off the prev_pitch
Helper function for get_new_pitch(). Returns a new_pitch based off the prev_pitch
Input: prev_pitch: int, the previous pitch | interval: int, the interval to shift the new_pitch | ascend_or_descend: int, dictates if the pitch is shifted up or down
Output: new_pitch: int, the pitch of the new note
"""
Expand All @@ -391,10 +393,9 @@ def get_new_pitch(prev_pitch: int, interval: int, ascend_or_descend: int) -> int

return new_pitch


def get_new_note_pitch(prev_pitch: int, ascend_or_descend: int, scale: list) -> int:
def get_new_pitch(prev_pitch: int, ascend_or_descend: int, scale: list) -> int:
"""
Helper function for next_note. Uses the previous note's pitch to dictate the pitch of the pitch it generates.
Helper function for get_new_note_pitch(). Uses the previous note's pitch to dictate the pitch of the pitch it generates.
Pitchs could also be a rest or a completely random new pitch in the scale, not associated with the previous pitch.
Input: prev_pitch: int, the previous pitch value | ascend_or_descend: int, dictates if the pitch is shifted up or down (0 = descend, 1 = ascend)
scale: list, the note pitches available in the current scale
Expand All @@ -413,36 +414,46 @@ def get_new_note_pitch(prev_pitch: int, ascend_or_descend: int, scale: list) ->
new_pitch = prev_pitch
elif option == 1:
# step
new_pitch = get_new_pitch(prev_pitch, 2, ascend_or_descend)
new_pitch = calculate_pitch(prev_pitch, 2, ascend_or_descend)

elif option == 2:
# third
new_pitch = get_new_pitch(prev_pitch, 3, ascend_or_descend)
new_pitch = calculate_pitch(prev_pitch, 3, ascend_or_descend)

elif option == 3:
# skip
skip = random.randint(1, 4)
new_pitch = get_new_pitch(prev_pitch, skip, ascend_or_descend)
new_pitch = calculate_pitch(prev_pitch, skip, ascend_or_descend)

elif option == 4:
# Octave
new_pitch = get_new_pitch(prev_pitch, 12, ascend_or_descend)
new_pitch = calculate_pitch(prev_pitch, 12, ascend_or_descend)

elif option == 5:
# jump
jump = random.randint(4, 14)
new_pitch = get_new_pitch(prev_pitch, jump, ascend_or_descend)
new_pitch = calculate_pitch(prev_pitch, jump, ascend_or_descend)

elif option == 6:
# random
new_pitch = random.choice(scale)
pitch_str = random.choice(scale)
new_pitch = NOTE_TO_MIDI[pitch_str]

else:
# rest
new_pitch = 128

return new_pitch


def get_new_note_pitch(prev_pitch: int, ascend_or_descend: int, scale: list) -> int:

new_pitch = get_new_pitch(prev_pitch, ascend_or_descend, scale)
while MIDI_TO_NOTE[new_pitch] not in NOTE_RANGE:
new_pitch = get_new_pitch(prev_pitch, ascend_or_descend, scale)

return new_pitch

def get_new_beat(prev_beats: float) -> float:
"""
Helper function for get_new_note_beat(). Returns a float for the new note's beat value.
Expand Down

0 comments on commit e1bccf2

Please sign in to comment.