예제 #1
0
def note2chord(chordlist):
    chord = []
    for i in sorted(chordlist):
        temp = utils.val_to_note(int(i) % 12)
        if temp not in chord:
            chord.append(temp)
    n = 0
    while n < 100:
        if note_to_chord(chord):
            return note_to_chord(chord)[0]
        else:
            random.shuffle(chord)
            n = n + 1
def getChords(notes):
    global cpt
    global baseChord

    #print(notes)

    chord = note_to_chord(notes)  # Convert notes to chord
    if len(chord) == 0:  # Si c'est pas un accord
        return  # Pas de changement de couleur
    chord = str(chord[0])  # Si c'est un accord le passe en String

    #print(chord)

    chord = list(chord)  # Si c'est un accord le passe en list
    """
	if "/" in chord and chord[-1] != "#" : # Si accord renverse sans # a la fondamentale
		chord[0] = chord[-1] # La fondamentale reprend leur place
	elif  "/" in chord and chord[-1] == "#": # Si accord renverse avec # a la fondamentale
			chord[0] = chord[-2] # La fondamentale et son # reprennent leur place
			chord[1] = chord[-1]
	"""
    if len(chord) > 1 and chord[1] == "#":  # Si c'est un accord avec un #
        chord = chord[0] + chord[
            1]  # L'accord prend comme nom sa fondamentale et son #
    else:
        chord = chord[0]  # L'accord prend comme non sa fondamentale

    if baseChord == "" or baseChord != chord:  # Si l'accord n'est pas le meme que l'ancien
        baseChord = chord  # L'accord devient l'accord de reference
    print(chord)
    return pickColorChord(chord)
예제 #3
0
def home():

    if request.method == 'GET':
        """return the information for <user_id>"""

        return myChord.pitchedCommonName

    if request.method == 'POST':
        """modify/update the information for <user_id>"""
        # you can use <user_id>, which is a str but could
        # changed to be int or whatever you want, along
        # with your lxml knowledge to make the required
        # changes
        if "c" in request.data:
            del notasActivas[:]
            del notasNombre[:]
            return str(len(notasNombre))

        notasActivas.append(request.data)

        for nota in notasActivas:

            p = pitch.Pitch(nota)
            p = p.name
            if p in notasNombre:

                test = 1

            else:
                notasNombre.append(p)

        str1 = ' '.join(notasNombre)

        for notas in notasNombre:
            print(notas)

        if str(note_to_chord(notasNombre)) == "[]":
            test = chord.Chord(notasNombre)

            return test.pitchedCommonName

        else:
            myChord = note_to_chord(notasNombre)
            print(str(myChord))

            return str(myChord)
예제 #4
0
def bin_to_chord(bit_arr):
    notes = NOTE_INTS_ARR[bit_arr > 0]
    note_names = [INT_TO_NOTE[n] for n in notes]
    try:
        chrd = note_to_chord(note_names)
    except:
        return []
    return chrd
예제 #5
0
    def get(self):
        midi_chord = request.args.getlist('notes')[0].split(',')
        pychord_chord = note_to_chord(
            [note_to_name(int(i)) for i in midi_chord])
        result_dict = {("chord_" + str(i)): str(pychord_chord[i])
                       for i in range(len(pychord_chord))}

        return jsonify(result_dict)
예제 #6
0
 def to_chord(self):
     if not self.chord:
         note_names = self.to_note_names()
         chords = note_to_chord(note_names)
         print(chords)
         if len(chords):
             self.chord = chords[0]
         else:
             self.chord = None
     return self.chord
예제 #7
0
파일: logic.py 프로젝트: uollazzi/armonia
def get_chords(mode, key, seventh=False, ninth=False):
    retval = []
    # PRINT THE CHORDS
    for i in range(7):
        chord = []
        chord.append(get_note_name(get_note_by_index(mode, i), key))
        chord.append(get_note_name(get_note_by_index(mode, i + 2), key))
        chord.append(get_note_name(get_note_by_index(mode, i + 4), key))

        # settima
        if seventh:
            chord.append(get_note_name(get_note_by_index(mode, i + 6), key))

        # nona
        if ninth:
            chord.append(get_note_name(get_note_by_index(mode, i + 1), key))

        retval.append(note_to_chord(chord))

    return retval
예제 #8
0
def get_chord_progression_from_file(f_name, debug=False):
    """
    Takes a file name returns a chord progression

    :return ChordProgression
    """

    if debug:
        print("processing: {}".format(f_name))
    mlt = Multitrack()
    try:
        mlt.parse_midi(filename=f_name, binarized=True)
        merged_mlt = mlt.get_merged_pianoroll(mode='max')
    except:
        print("unable to parse. skipping: {}".format(f_name))
        return ChordProgression()  # empty progression

    chord_progression = ChordProgression([])
    last_chord_change = 0
    for i, x in enumerate(merged_mlt):
        # print(x)
        notes = np.where(x)
        num_notes = np.sum(x)
        if num_notes:
            if num_notes > 1:
                chord_notes = INT_TO_NOTE[notes[0] % 12]
                chord_name = note_to_chord(chord_notes.tolist())
                if chord_name:
                    chord = chord_name[0]
                    if len(chord_progression.chords
                           ) == 0 or chord_progression.chords[-1] != chord:
                        # print("chord: ", notes[0], chord_notes, chord_name)
                        chord_progression.append(chord)
            # else:
            #     print("note: ", notes[0])
    # print(chord_progression)
    return chord_progression
예제 #9
0
def getChords(notes):
    if len(notes) > 1:  # If two notes or more know the chord
        if len(note_to_chord(notes)) != 0:
            return pickColorChord(str(note_to_chord(notes)[0]))
예제 #10
0
def record_midi_menu(WIN):
    # the velocity with which the notes move up
    note_vel = 2

    # list of currently pressed notes, used for drawing
    notes = []

    # list of currently pressed notes, used for chords
    notes_pressed = []

    # particles list
    particles = []

    pianoKeyboard = PianoKeyboard()
    pianoKeyboard.init()

    # clear input buffer
    midis2events.midis2events(MIDI_INPUT.read(40), MIDI_INPUT).clear()

    # start time of the function
    start = t.perf_counter()

    click = False

    mf = None
    track = None
    channel = None

    running = True
    recording = False
    entering_text = False
    while running:
        # fill background color and draw keyboard over it
        WIN.fill(TAN)
        pianoKeyboard.draw(WIN, notes_pressed)

        # draw notes
        for note in notes:
            if note.is_pressed:
                note.incrementHeight(note_vel)
                note.drawNote(WIN)
                # generate particles
                COLOR = None
                if note.is_white:
                    if note.number < 60:
                        COLOR = COLOR1
                    else:
                        COLOR = COLOR3
                else:
                    if note.number < 60:
                        COLOR = COLOR1
                    else:
                        COLOR = COLOR3

                p = Particle([
                    pianoKeyboard.keys[note.number].x + 6,
                    HEIGHT - WHITE_NOTE_HEIGHT
                ], random.randint(1, 5), [
                    random.randint(0, 20) / 10 - 1,
                    random.randint(0, 20) / 10 - 2
                ], 6, RED)
                particles.append(p)
            else:
                note.moveNoteUp(note_vel)
                note.drawNote(WIN)

            if note.rect.y + note.rect.h + 100 < 0:
                notes.remove(note)

        # remove expired particles
        for particle in particles:
            if particle.radius <= 0:
                particles.remove(particle)

        # draw top bar
        pygame.draw.rect(WIN, WHITE, (0, 0, WIDTH, HEIGHT // 17))
        pygame.draw.rect(WIN, BLACK, (0, HEIGHT // 17, WIDTH, 2))

        # draw text on bar
        draw_text(WIN, "Record MIDI", 25, WIDTH // 2, HEIGHT // 17 // 2, BLACK,
                  True, True)
        back_button = draw_button('Main Menu', FONT_NAME, 25, BLACK, WIN,
                                  WIDTH - 5, HEIGHT // 17)

        if not recording:
            draw_text(WIN, "Press P to start recording", 20, WIDTH - 5,
                      HEIGHT // 17 * 1.5, BLACK, True, False, True)
        else:
            draw_text(WIN, "Press P to stop recording", 20, WIDTH - 5,
                      HEIGHT // 17 * 1.5, BLACK, True, False, True)

        # draw currently pressed chord name on screen
        if len(notes_pressed) > 0:
            notes_pressed_char = []
            for note in notes_pressed:
                notes_pressed_char.append(number_to_note(note))

            notes_pressed_char.sort()
            if len(notes_pressed_char) == 1:
                draw_text(WIN, notes_pressed_char[0], 25, 5, HEIGHT // 17 // 2,
                          BLACK, True, False)
            elif len(notes_pressed_char) == 2 and abs(notes_pressed[0] -
                                                      notes_pressed[1]) == 12:
                draw_text(WIN, notes_pressed_char[0] + " with perfect octave",
                          25, 5, HEIGHT // 17 // 2, BLACK, True, False)
            elif note_to_chord(notes_pressed_char):
                # REGEX
                chords = re.findall(r'<Chord: ([^>]*)',
                                    str(note_to_chord(notes_pressed_char)))
                text = ", ".join(chords)
                draw_text(WIN, "Chord: " + text, 25, 5, HEIGHT // 17 // 2,
                          BLACK, True, False)

        # draw particles
        for particle in particles:
            particle.position[0] += particle.velocity[0]
            particle.position[1] += particle.velocity[1]
            particle.radius -= 0.1
            pygame.draw.circle(WIN, particle.color, particle.position,
                               particle.radius)
            if particle.radius <= 0:
                particles.remove(particle)

        # check for midi events from the input port
        for event in midis2events.midis2events(MIDI_INPUT.read(40),
                                               MIDI_INPUT):

            # for some reason, all events are NOTE_ON events
            # a NOTE_ON event with velocity 0 represents a NOTE_OFF event
            if event.command == midis2events.NOTE_ON:
                # extract data from event
                note_number = event.data1
                velocity = event.data2

                if velocity != 0:  # note on
                    print(f"ON %s %s %s" %
                          (event.data1, velocity, t.perf_counter() - start))

                    if pianoKeyboard.keys[note_number].is_white:
                        WHITE_NOTE_RECT = pygame.Rect(
                            (pianoKeyboard.keys[note_number].x + 6,
                             HEIGHT - WHITE_NOTE_HEIGHT),
                            (WIDTH // NR_WHITE_NOTES - 3, 0))
                        note = Note(number=note_number,
                                    velocity=velocity,
                                    start_time=t.perf_counter() - start,
                                    rect=WHITE_NOTE_RECT,
                                    is_pressed=True,
                                    is_white=True)
                    else:
                        BLACK_NOTE_RECT = pygame.Rect(
                            (pianoKeyboard.keys[note_number].x + 3,
                             HEIGHT - WHITE_NOTE_HEIGHT),
                            (WIDTH // 100 - 3, 0))
                        note = Note(number=note_number,
                                    velocity=velocity,
                                    start_time=t.perf_counter() - start,
                                    rect=BLACK_NOTE_RECT,
                                    is_pressed=True,
                                    is_white=False)

                    # add note to notes list
                    notes.append(note)

                    # add note number to currently pressed notes list
                    notes_pressed.append(note_number)

                else:  # note off
                    # search for note in notes list and set note.is_pressed to False
                    for note in notes:
                        if note.number == note_number and note.is_pressed:
                            note.is_pressed = False

                            # write note to MIDI file
                            try:
                                if recording:
                                    mf.addNote(
                                        track, channel, note_number,
                                        note.start_time,
                                        t.perf_counter() - start -
                                        note.start_time, note.velocity)
                            except:
                                print(
                                    "Something went wrong with writing the MIDI file."
                                )

                            # remove note from notes_pressed list
                            notes_pressed.remove(note_number)

                            print("turn off note %s with start time %s" %
                                  (note.number, note.start_time))

                    print(f"OFF %s %s %s" %
                          (event.data1, event.data2, t.perf_counter() - start))
            # elif event.command == midis2events.NOTE_OFF:

        click = False
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False

                if event.key == pygame.K_p and recording == False:
                    recording = True
                    # create MIDI object
                    mf = MIDIFile(1)  # only 1 track
                    track = 0  # the only track
                    mf.addTrackName(track, 0, "Sample Track")
                    mf.addTempo(track, 0, 60)
                    channel = 0
                    start = t.perf_counter()

                elif event.key == pygame.K_p and recording == True:
                    recording = False
                    filename = enter_text(WIN)
                    if filename != "":
                        with open(filename + ".mid", 'wb') as outf:
                            mf.writeFile(outf)

            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    click = True

        mx, my = pygame.mouse.get_pos()
        if back_button.collidepoint((mx, my)) and click:
            running = False

        pygame.display.update()
        CLOCK.tick(FPS)
예제 #11
0
from pychord import Chord, note_to_chord

chrd1 = Chord("Am7")
chrd2 = Chord("C")

print(chrd1)

print(chrd2)

print(note_to_chord(["C", "E", "G"]))
예제 #12
0
파일: midi.py 프로젝트: spinner0929/MIDI
    i = pygame.midi.Input(input_id)
    print("input MIDI:%d" % input_id)
    print("starting")

    going = True
    pre_status = 156
    inputs = []

    while going:
        if i.poll():
            midi_events = i.read(
                10
            )  # midi_events = [[[status,note,velocity,channel],timestamp],...]
            if (midi_events[0][0][1] == 96):  # C7 押下で終了
                going = False
            elif (midi_events[0][0][0] == 156):  # 鍵盤を押下したとき
                inputs.append(midi_events[0][0][1])
            elif (midi_events[0][0][0] == 140):  # 鍵盤を離したとき
                if (pre_status == 156):  # 鍵盤を押下していたとき
                    inputs.sort()
                    notes = convert(inputs)
                    notes_list = sorted(set(notes), key=notes.index)
                    print(notes_list, note_to_chord(notes_list))
                inputs = []
            pre_status = midi_events[0][0][0]  # 前の status を保存する

    i.close()
    pygame.midi.quit()
    pygame.quit()
    exit()
예제 #13
0
 def test_find_from_components(self):
     self.quality_manager.set_quality("13", (0, 4, 7, 10, 14, 17, 21))
     chords = note_to_chord(['C', 'E', 'G', 'Bb', 'D', 'F', 'A'])
     self.assertEqual(chords, [Chord("C13")])
from pychord import Chord
import pychord

chord_am7 = Chord("Am7")
chord_am7_info = chord_am7.info()

chord_am7_comp = chord_am7.components()

bbb = pychord.note_to_chord(["C", "E", "G"])
print(chord_am7_info)
예제 #15
0
def serve(model_path=MODEL_OUT_PATH, in_port_name=None, out_port_name=None):
    if not model_path:
        print("FastText model is required!")
    if not in_port_name:
        print("MIDI input port name is required!")
    if not out_port_name:
        print("MIDI output port name is required!")

    mdl = load_mdl(model_path)

    INPUT_DEVICE_NAME = in_port_name

    OUTPUT_DEVICE_NAME = out_port_name

    print('MIDI ports: in: {}, out: {}'.format(INPUT_DEVICE_NAME,
                                               OUTPUT_DEVICE_NAME))
    port = mido.open_output(OUTPUT_DEVICE_NAME)
    inport = mido.open_input(INPUT_DEVICE_NAME)

    current_ai_notes = set()
    current_human_played_notes = set()
    current_human_played_note = None
    current_human_vel = 0
    last_human_played_note = None

    while True:
        r_msg = inport.receive()
        if r_msg.type in ['note_on', 'note_off']:
            note_num = INT_TO_NOTE[r_msg.note % 12]
            if r_msg.type == 'note_on':
                current_human_played_notes.add(r_msg.note)
                last_human_played_note = current_human_played_note
                current_human_played_note = r_msg.note
                current_human_vel = r_msg.velocity
            else:
                if r_msg.note in current_human_played_notes:
                    stop_one_note(port, r_msg.note)
                    current_human_played_notes.remove(r_msg.note)
                current_human_played_note = None
                current_human_vel = 0

        # print(current_human_played_notes)
        # print(current_human_played_note, "vel: ", current_human_vel)

        if not current_human_played_notes:
            stop_all_ai_notes(port, list(current_ai_notes))
            current_ai_notes = set()
        elif current_human_played_note and current_human_played_note != last_human_played_note:
            chrd_notes = [current_human_played_note]
            human_octave = int(max(current_human_played_notes) / 12)
            if len(current_human_played_notes) == 1:
                # chrd = Chord(chrd_notes[0])
                chrd_str = INT_TO_NOTE[chrd_notes[0] % 12]
                human_notes_names = [chrd_str]
            else:
                human_notes = list(current_human_played_notes)
                human_notes_names = [INT_TO_NOTE[n % 12] for n in human_notes]
                chrd = note_to_chord(human_notes_names)
                if not chrd:
                    chrd_str = ''
                else:
                    chrd_str = str(chrd[0])
            if not chrd_str or chrd_str == '</s>':
                continue
            try:
                next_chord = get_next_chord(mdl,
                                            chrd_str,
                                            random_neighbors=True,
                                            hypercube=False)
                if not next_chord:
                    continue
                print("notes: {}, chord_suggestion: {}".format(
                    human_notes_names, next_chord))
                next_chord_notes = generate_notes_from_chord(
                    next_chord, human_octave)
                # stop any pending AI notes
                stop_all_notes(port)
                current_ai_notes = set()

                # start new AI notes
                start_all_ai_notes(port, next_chord_notes, current_human_vel)
                for note in next_chord_notes:
                    current_ai_notes.add(note)
            except:
                print("error parsing chrd_str: '{}'".format(chrd_str))
예제 #16
0
    bestfreqs = freq[sortorder[-10:]]

    freq1 = bestfreqs[-1]
    freq2 = bestfreqs[-2]
    freq3 = bestfreqs[-3]
    freq4 = bestfreqs[-4]
    note1, octave1 = findnote(freq1, df)
    note2, octave2 = findnote(freq2, df)
    note3, octave3 = findnote(freq3, df)
    note4, octave4 = findnote(freq4, df)

    freqs = [freq1, freq2, freq3, freq4]
    octaves = np.asarray([octave1, octave2, octave3, octave4])
    notes = np.asarray([note1, note2, note3, note4])

    sortfreq = np.argsort(freqs)

    sortednotes = list(notes[sortfreq])
    sortedoctaves = list(octaves[sortfreq])

    #now need to find the root note. start by finding lowest frequency.

    finalnotes = list(dict.fromkeys(sortednotes))

    print('this chord consists of the notes: ' + sortednotes[0] +
          sortedoctaves[0] + ' ' + sortednotes[1] + sortedoctaves[1] + ' ' +
          sortednotes[2] + sortedoctaves[2] + ' ' + sortednotes[3] +
          sortedoctaves[3])

    print('The chord is ' + str(note_to_chord(finalnotes)))
def analyze_chunk(interval, fs, data_parts, duration,
                  T1):  # rekurzivna funkcija

    global current_time, k, data_chunk  # current time, current position in data_parts array , data chunk to be analyzed
    global start, end  # starting and ending position of chunk to be analyzed
    global once  # switch
    global chord_prog, durations  # list to store chords that are found and list to store durations

    #T2 = time.time()
    #if ((T2 - T1) > 60):
    #return "Failed"

    if (current_time >= duration or k == len(data_parts)):  # basic if
        return "Done"

    if (once):  # running once, to make starting interval
        end = start + interval
        once = False

    chunk = end - start  # deo u sekundi

    n_intervals = round(chunk / interval)

    if (n_intervals > 1):
        for i in range(0, n_intervals):
            if (k != len(data_parts)):
                data_chunk = np.append(data_chunk, data_parts[k])
                k = k + 1
            else:
                print("Nothing found beetwen {0} and {1}".format(
                    round(start, 1), round(end, 1)))
                chord_prog.append("None")
                durations.append(round(chunk, 1) - interval)
                return "Done"
    else:
        data_chunk = data_parts[k]
        k = k + 1

    fft_and_freq = do_fft(
        fs, data_chunk
    )  # kreiranje matrice sa fft vrednostima i odgovarajucim frekvencijama

    notes = find_all_notes(
        fft_and_freq[0], fft_and_freq[1]
    )  # trazenje svih nota, sortiranih od one koja se cuje najvise

    triad_notes = []
    l = 0
    for note in notes:
        if (note != "None" and l < 3):
            triad_notes.append(note)
            l = l + 1

    triad = reference_sort(
        triad_notes
    )  # pravljenje trikorda od 3 najjaca tona, sortirana po hromatskoj skali

    chord_list = note_to_chord(
        triad
    )  # odredjivanje koji to akord, s tim da note_to_chord funkcija vraca listu (len(a) = 0 akko nije nasao akord)

    if (len(chord_list) != 0):  # ako nadje akord
        chord = chord_list[0].chord

        print("Chord {0} found between {1} - {2}".format(
            chord, round(start, 1), round(end, 1)))
        #print("Notes found are ", triad)

        if ("/" in chord):  # sklanjanje "/"
            slash_id = chord.find("/")
            chord = chord[0:slash_id]
        else:
            pass

        current_time = current_time + chunk

        chord_prog.append(chord)
        durations.append(round(chunk, 1))

        data_chunk = []  # emptying data chunk to analyzed

        start = end  # moving start point to end
        end = end + interval  # moving end point by 1 interval

        # increasing analyzed_time by analyzed time

        analyze_chunk(interval, fs, data_parts, duration, T1)

    else:
        data_chunk = []  # emptying data chunk to analyzed

        end = end + interval  # moving end point by 1 interval

        #print("Nothing found, increasing interval to {0}".format(round(end - start, 1)))

        k = k - n_intervals

        analyze_chunk(interval, fs, data_parts, duration, T1)
def find_chord_progression(interval, fs, data, is_stereo=True):

    duration = len(data) / fs  # duzina uzorka (s)

    n_parts = round(duration / interval)  # broj delova

    data_parts = np.array_split(data, n_parts)  # seckanje

    j = 0
    start_time = 0
    end_time = interval

    for i in range(0, n_parts):
        fft_and_freq = do_fft(
            fs, data_parts[i], is_stereo
        )  # kreiranje matrice sa fft vrednostima i odgovarajucim frekvencijama

        notes = find_all_notes(
            fft_and_freq[0], fft_and_freq[1]
        )  # trazenje svih nota, sortiranih od one koja se cuje najvise

        triad = reference_sort(
            [notes[0], notes[1], notes[2]], chrom_scale
        )  # pravljenje trikorda od 3 najjaca tona, sortirana po hromatskoj skali

        chord_list = note_to_chord(
            triad
        )  # odredjivanje koji to akord, s tim da note_to_chord funkcija vraca listu

        # ako su random note i funkcija ne moze da nadje akord, crashuje
        try:
            chord = chord_list[0].chord
        except:
            chord = "None"

        # ispisivanje tonova
        # print("Notes found: ", reference_sort(notes[0:3], chrom_scale))

        # zbog mogucih inverzija na klaviru pojavljuju se akordi poput
        # C/G sto ustvari jesu tonovi C,E,G mada je G bas

        if ("/" in chord):
            slash_id = chord.find("/")
            chord = chord[0:slash_id]

            print(chord[0:slash_id], " ", start_time, " - ", end_time, "s")
        else:
            print(chord, " ", start_time, " - ", end_time, "s")

        # deo koji racuna trajanje svakog akorda
        if (i == 0):
            durations.append(0.0)
            durations[j] = durations[j] + interval

            chord_prog.append(chord)

            prev_chord = chord
            # print("Added 0.2s to chord: ", prev_chord, "\n")

        if (i != 0):
            if (prev_chord == chord):
                durations[j] = durations[j] + interval
                # print("Same as the last one, added 0.2s to chord: ", prev_chord, "\n")

            else:
                prev_chord = chord
                chord_prog.append(chord)
                # print("Chord changed to: ", prev_chord)

                j = j + 1
                durations.append(0.0)
                durations[j] = durations[j] + interval
                # print("Added 0.2s to chord: ", prev_chord, "\n")

        start_time = round(start_time + interval, 1)
        end_time = round(end_time + interval, 1)

        if (i == n_parts - 1):
            k = 0
            for el in durations:
                durations[k] = round(durations[k], 1)
                k = k + 1
예제 #19
0
def freeplay_menu(WIN):
    # the velocity with which the notes move up
    note_vel = 2

    # list of currently pressed notes, used for drawing chords to screen
    notes = []

    # list of currently pressed notes, used for chords
    notes_pressed = []

    # particles list
    particles = []

    pianoKeyboard = PianoKeyboard()
    pianoKeyboard.init()

    # clear input buffer
    midis2events.midis2events(MIDI_INPUT.read(40), MIDI_INPUT).clear()

    # start time of the function
    start = t.perf_counter()

    click = False

    running = True
    while running:

        # fill background color and draw keyboard over it
        WIN.fill(TAN)
        pianoKeyboard.draw(WIN, notes_pressed)

        # draw notes
        for note in notes:
            if note.is_pressed:
                note.incrementHeight(note_vel)
                note.drawNote(WIN)
                # generate particles
                COLOR = RED

                p = Particle([pianoKeyboard.keys[note.number].x + 6, HEIGHT - WHITE_NOTE_HEIGHT], random.randint(1, 5),
                             [random.randint(0, 20) / 10 - 1, random.randint(0, 20) / 10 - 2], 6, COLOR)
                particles.append(p)
            else:
                note.moveNoteUp(note_vel)
                note.drawNote(WIN)

            if note.rect.y + note.rect.h + 100 < 0:
                notes.remove(note)

        # remove expired particles
        for particle in particles:
            if particle.radius <= 0:
                particles.remove(particle)

        # draw top bar
        pygame.draw.rect(WIN, WHITE, (0, 0, WIDTH, HEIGHT // 17))
        pygame.draw.rect(WIN, BLACK, (0, HEIGHT // 17, WIDTH, 2))

        # draw text on bar
        draw_text(WIN, "Free Play", 25, WIDTH // 2, HEIGHT // 17 // 2, BLACK, True, True)
        back_button = draw_button('Main Menu', FONT_NAME, 25, BLACK, WIN, WIDTH - 5, HEIGHT // 17)

        # draw currently pressed chord name on screen
        if len(notes_pressed) > 0:
            notes_pressed_char = []
            for note in notes_pressed:
                notes_pressed_char.append(number_to_note(note))

            notes_pressed_char.sort()

            if len(notes_pressed_char) == 1:
                draw_text(WIN, notes_pressed_char[0], 25, 5, HEIGHT // 17 // 2, BLACK, True, False)
            elif len(notes_pressed_char) == 2 and abs(notes_pressed[0] - notes_pressed[1]) == 12:
                draw_text(WIN, notes_pressed_char[0] + " with perfect octave", 25, 5, HEIGHT // 17 // 2, BLACK, True,
                          False)
            else:
                notes_pressed_char = remove_duplicates(notes_pressed_char)
                if note_to_chord(notes_pressed_char):
                    # REGEX
                    chords = re.findall(r'<Chord: ([^>]*)', str(note_to_chord(notes_pressed_char)))
                    text = ", ".join(chords)
                    draw_text(WIN, "Chord: " + text, 25, 5, HEIGHT // 17 // 2, BLACK, True, False)

            print(remove_duplicates(["a", "b", "a", "c", "c"]))

        # draw particles
        for particle in particles:
            particle.position[0] += particle.velocity[0]
            particle.position[1] += particle.velocity[1]
            particle.radius -= 0.1
            pygame.draw.circle(WIN, particle.color, particle.position, particle.radius)
            if particle.radius <= 0:
                particles.remove(particle)

        # check for midi events from the input port
        for event in midis2events.midis2events(MIDI_INPUT.read(40), MIDI_INPUT):

            # for some reason, all events are NOTE_ON events
            # a NOTE_ON event with velocity 0 represents a NOTE_OFF event
            if event.command == midis2events.NOTE_ON:

                # extract data from event
                note_number = event.data1
                velocity = event.data2

                if velocity != 0:  # note on
                    print(f"ON %s %s %s" % (event.data1, velocity, t.perf_counter() - start))
                    if pianoKeyboard.keys[note_number].is_white:
                        WHITE_NOTE_RECT = pygame.Rect((pianoKeyboard.keys[note_number].x + 6,
                                                       HEIGHT - WHITE_NOTE_HEIGHT), (WIDTH // NR_WHITE_NOTES - 3, 0))
                        note = Note(number=note_number, velocity=velocity, start_time=t.perf_counter() - start,
                                    rect=WHITE_NOTE_RECT, is_pressed=True, is_white=True)

                    else:
                        BLACK_NOTE_RECT = pygame.Rect((pianoKeyboard.keys[note_number].x + 3,
                                                       HEIGHT - WHITE_NOTE_HEIGHT), (WIDTH // 100 - 3, 0))
                        note = Note(number=note_number, velocity=velocity, start_time=t.perf_counter() - start,
                                    rect=BLACK_NOTE_RECT, is_pressed=True, is_white=False)

                    # add note to notes list
                    notes.append(note)

                    # add note number to currently pressed notes list
                    notes_pressed.append(note_number)

                else:  # note off
                    # search for note in notes vector and set is_pressed to False
                    for note in notes:
                        if note.number == note_number and note.is_pressed:
                            note.is_pressed = False

                    # remove note from notes_pressed list
                    notes_pressed.remove(note_number)

                    print(f"OFF %s %s %s" % (event.data1, event.data2, t.perf_counter() - start))
            # elif event.command == midis2events.NOTE_OFF:

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    click = True

        mx, my = pygame.mouse.get_pos()
        if back_button.collidepoint((mx, my)) and click:
            running = False

        pygame.display.update()
        CLOCK.tick(FPS)