File size: 2,932 Bytes
07f2491
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import gradio as gr
import mido
from mido import Message, MidiFile, MidiTrack
import numpy as np
import os
import matplotlib.pyplot as plt


def generate_chords(hexachord):
    # Placeholder for your actual chord generation function
    # Assuming hexachord is a list of MIDI note numbers
    chords = []
    for i in range(4):  # Generate 4 chords
        chords.append([note + (i * 2) for note in hexachord])  # Simple transposition
    return chords


def create_midi(chords):
    mid = MidiFile()
    track = MidiTrack()
    mid.tracks.append(track)

    for chord in chords:
        for note in chord:
            track.append(Message('note_on', note=int(note), velocity=64, time=0))
        for note in chord:
            track.append(Message('note_off', note=int(note), velocity=64, time=480))

    midi_path = "output.mid"
    mid.save(midi_path)
    return midi_path


def generate_piano_roll(chords):
    fig, ax = plt.subplots(figsize=(8, 4))

    for i, chord in enumerate(chords):
        for note in chord:
            ax.broken_barh([(i * 1, 0.8)], (note - 0.4, 0.8), facecolors='blue')

    ax.set_xlabel("Chord Progression")
    ax.set_ylabel("MIDI Note Number")
    ax.set_yticks(range(min(min(chords)), max(max(chords)) + 1, 2))
    ax.set_xticks(range(len(chords)))
    ax.set_xticklabels([f"Chord {i + 1}" for i in range(len(chords))])
    ax.invert_yaxis()

    plt.grid(True, linestyle='--', alpha=0.5)
    plt.savefig("piano_roll.png")
    return "piano_roll.png"


def process_hexachord(note1, note2, note3, note4, note5, note6):
    notes = [note1, note2, note3, note4, note5, note6]
    notes = [int(note) for note in notes if note is not None]  # Convert to int, remove None values

    if len(notes) != 6 or len(set(notes)) != 6:
        return "Please select exactly 6 unique notes."

    chords = generate_chords(notes)
    midi_path = create_midi(chords)
    piano_roll_path = generate_piano_roll(chords)

    return midi_path, piano_roll_path


# UI Components
note_options = list(range(21, 109))  # MIDI note numbers
default_notes = [60, 62, 64, 65, 67, 69]  # Default MIDI notes for C major hexachord

with gr.Blocks() as ui:
    gr.Markdown("# Hexachord-based Chord Generator")

    with gr.Row():
        note_inputs = [gr.Dropdown(choices=note_options, label=f"Note {i + 1}", value=default_notes[i]) for i in
                       range(6)]

    generate_button = gr.Button("Generate Chords")
    midi_output = gr.File(label="Generated MIDI")
    piano_roll_output = gr.Image(label="Piano Roll Visualization")

    generate_button.click(
        fn=process_hexachord,
        inputs=note_inputs,
        outputs=[midi_output, piano_roll_output]
    )

# Detect if running on Hugging Face Spaces
on_huggingface = "HUGGINGFACE_SPACE" in os.environ


def launch_app():
    if on_huggingface:
        ui.launch(server_name="0.0.0.0", server_port=7860)
    else:
        ui.launch()


launch_app()