Krish Shah-Nathwani commited on
Commit
2d0e212
·
1 Parent(s): 024ffaf

Adding in HF files and updating README.md

Browse files
Files changed (4) hide show
  1. .gitattributes +35 -0
  2. README.md +21 -2
  3. app.py +201 -0
  4. requirements.txt +1 -0
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,2 +1,21 @@
1
- # Chord-Bot
2
- say note make chord. Wow!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ---
2
+
3
+ # title: "Note → Chord Chatbot"
4
+
5
+ # emoji: "🎵"
6
+
7
+ # colorFrom: blue
8
+
9
+ # colorTo: purple
10
+
11
+ # sdk: gradio
12
+
13
+ # sdk\_version: "4.44.0"
14
+
15
+ # app\_file: app.py
16
+
17
+ # pinned: false
18
+
19
+ # ---
20
+
21
+
app.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import gradio as gr
3
+ from typing import List, Tuple
4
+ import re
5
+
6
+ # ----- Pitch utilities -----
7
+ SHARP_NAMES = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"]
8
+ FLAT_NAMES = ["C","Db","D","Eb","E","F","Gb","G","Ab","A","Bb","B"]
9
+
10
+ NAME_TO_PC = {
11
+ # naturals
12
+ "C":0, "D":2, "E":4, "F":5, "G":7, "A":9, "B":11,
13
+ # sharps
14
+ "C#":1, "D#":3, "F#":6, "G#":8, "A#":10,
15
+ # flats
16
+ "Db":1, "Eb":3, "Gb":6, "Ab":8, "Bb":10,
17
+ # unicode ♯/♭
18
+ "C♯":1, "D♯":3, "F♯":6, "G♯":8, "A♯":10,
19
+ "D♭":1, "E♭":3, "G♭":6, "A♭":8, "B♭":10,
20
+ }
21
+
22
+ # Common chord templates: set of intervals from root (0 always present)
23
+ CHORD_TEMPLATES = {
24
+ (0,4,7): ("major triad", ""),
25
+ (0,3,7): ("minor triad", "m"),
26
+ (0,3,6): ("diminished triad", "dim"),
27
+ (0,4,8): ("augmented triad", "+"),
28
+ (0,2,7): ("sus2", "sus2"),
29
+ (0,5,7): ("sus4", "sus4"),
30
+ (0,4,7,10): ("dominant 7th", "7"),
31
+ (0,4,7,11): ("major 7th", "maj7"),
32
+ (0,3,7,10): ("minor 7th", "m7"),
33
+ (0,3,6,10): ("half-diminished (m7♭5)", "m7b5"),
34
+ (0,3,6,9): ("diminished 7th", "dim7"),
35
+ (0,3,7,11): ("minor major 7th", "m(maj7)"),
36
+ (0,4,7,9): ("major 6th", "6"),
37
+ (0,3,7,9): ("minor 6th", "m6"),
38
+ }
39
+
40
+ ADD_TONES = {
41
+ 2: ("add9", "add9"), # treat 2 as add9
42
+ 9: ("add9", "add9"),
43
+ 11: ("add11", "add11"),
44
+ 6: ("add13", "add13"),
45
+ }
46
+
47
+ SUS_OVERLAPS = {(0,2,7), (0,5,7)}
48
+
49
+ NOTE_TOKEN_RE = re.compile(r"[A-Ga-g](?:#|b|♯|♭)?")
50
+
51
+
52
+ def pc_name(pc: int, prefer_flats: bool) -> str:
53
+ return (FLAT_NAMES if prefer_flats else SHARP_NAMES)[pc % 12]
54
+
55
+
56
+ def parse_notes(user_text: str) -> Tuple[List[int], bool, List[str]]:
57
+ """Return (pitch_classes, prefer_flats, original_tokens)."""
58
+ tokens = NOTE_TOKEN_RE.findall(user_text)
59
+ tokens = [t.upper().replace("♯", "#").replace("♭", "b") for t in tokens]
60
+ prefer_flats = any("B" in t and len(t)>1 for t in tokens) or any("b" in t for t in tokens)
61
+ pcs = []
62
+ for t in tokens:
63
+ if t in NAME_TO_PC:
64
+ pcs.append(NAME_TO_PC[t])
65
+ # dedupe while preserving order
66
+ seen = set()
67
+ pcs_unique = []
68
+ for p in pcs:
69
+ if p not in seen:
70
+ pcs_unique.append(p)
71
+ seen.add(p)
72
+ return pcs_unique, prefer_flats, tokens
73
+
74
+
75
+ def intervals_from_root(pcs: List[int], root: int) -> Tuple[int,...]:
76
+ ints = sorted(((p - root) % 12) for p in pcs)
77
+ if 0 not in ints:
78
+ ints = (0,) + tuple(i for i in ints)
79
+ return tuple(sorted(set(ints)))
80
+
81
+
82
+ def describe_chord(pcs: List[int], prefer_flats: bool) -> str:
83
+ if len(pcs) < 3:
84
+ return "Please provide at least 3 distinct note names (e.g., C E G or C, Eb, G)."
85
+
86
+ matches = []
87
+ for root in pcs: # try each included pitch as potential root
88
+ base_ints = intervals_from_root(pcs, root)
89
+ # Try exact template match first (triads/sevenths/6ths/etc.)
90
+ if base_ints in CHORD_TEMPLATES:
91
+ qual_name, suffix = CHORD_TEMPLATES[base_ints]
92
+ matches.append((root, qual_name, suffix, []))
93
+ continue
94
+ # Try triad with added tones (add9/add11/add13)
95
+ # Identify a triad subset and extra tones
96
+ for triad in [(0,4,7), (0,3,7), (0,3,6), (0,4,8)]:
97
+ triad_set = set(triad)
98
+ if triad_set.issubset(set(base_ints)):
99
+ extras = sorted(set(base_ints) - triad_set)
100
+ add_suffixes = []
101
+ for e in extras:
102
+ if e in ADD_TONES:
103
+ add_suffixes.append(ADD_TONES[e][1])
104
+ if add_suffixes:
105
+ qual_name, suffix = CHORD_TEMPLATES.get(triad, ("triad",""))
106
+ matches.append((root, f"{qual_name} with {'/'.join(add_suffixes)}", suffix + ("" if not add_suffixes else ("("+" ".join(add_suffixes)+")")), extras))
107
+ # sus chords (if match) possibly with added tones
108
+ for sus in [(0,2,7), (0,5,7)]:
109
+ if set(sus).issubset(set(base_ints)):
110
+ extras = sorted(set(base_ints) - set(sus))
111
+ add_suffixes = []
112
+ for e in extras:
113
+ if e in ADD_TONES:
114
+ add_suffixes.append(ADD_TONES[e][1])
115
+ qual_name, suffix = CHORD_TEMPLATES[sus]
116
+ matches.append((root, qual_name + (" with "+"/".join(add_suffixes) if add_suffixes else ""), suffix + ("" if not add_suffixes else ("("+" ".join(add_suffixes)+")")), extras))
117
+
118
+ if not matches:
119
+ # fallback: show interval set relative to lowest note as a hint
120
+ root_guess = min(pcs)
121
+ ints = intervals_from_root(pcs, root_guess)
122
+ return (
123
+ "I couldn't confidently name that chord. Interval set from {}: {}.\n"
124
+ "Try removing extensions/duplicates or check note spelling (sharps vs flats)."
125
+ ).format(pc_name(root_guess, prefer_flats), ",".join(str(i) for i in ints))
126
+
127
+ # Rank matches: prefer templates with no ambiguous extras, prefer 7th/6th over triad with adds, then by root being lowest provided note
128
+ def rank(m):
129
+ root, qual_name, suffix, extras = m
130
+ score = 0
131
+ if "7" in suffix or "6" in suffix:
132
+ score += 2
133
+ if not extras:
134
+ score += 1
135
+ if root == min(pcs):
136
+ score += 0.5
137
+ return -score
138
+
139
+ matches.sort(key=rank)
140
+
141
+ # Build a readable response listing top 1-3 candidates
142
+ top = matches[:3]
143
+ lines = []
144
+ for i,(root, qual_name, suffix, extras) in enumerate(top, start=1):
145
+ name = pc_name(root, prefer_flats)
146
+ symbol = name + (suffix if suffix else "")
147
+ spelled = ", ".join(pc_name(p, prefer_flats) for p in sorted(set(pcs)))
148
+ lines.append(f"{i}. {symbol} — {qual_name} (notes: {spelled})")
149
+
150
+ # Inversion hint (best-effort): if lowest note isn't the chosen root, suggest slash chord
151
+ chosen_root = top[0][0]
152
+ lowest = min(pcs)
153
+ if lowest != chosen_root:
154
+ lines[0] += f" — likely {pc_name(chosen_root, prefer_flats)}/{pc_name(lowest, prefer_flats)}"
155
+
156
+ return "\n".join(lines)
157
+
158
+
159
+ def answer(message: str, history: List[Tuple[str,str]]):
160
+ pcs, prefer_flats, tokens = parse_notes(message)
161
+ if not tokens:
162
+ return (
163
+ "Tell me 3+ notes (e.g., 'C E G' or 'Db, F, Ab, C'). "
164
+ "I support #/b and unicode ♯/♭."
165
+ )
166
+ return describe_chord(pcs, prefer_flats)
167
+
168
+
169
+ def example_inputs():
170
+ return [
171
+ "C E G",
172
+ "D F# A C",
173
+ "C Eb G Bb",
174
+ "F A C D",
175
+ "G Bb D F",
176
+ "Db F Ab C",
177
+ "C D G",
178
+ "A C E G#",
179
+ "E G# B D",
180
+ "C Eb Gb A"
181
+ ]
182
+
183
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
184
+ gr.Markdown("""
185
+ # 🎵 Note → Chord Chatbot
186
+ Type 3 or more note names and I'll guess the chord (triads, 7ths, 6ths, sus, and common add tones).
187
+ - Accepted formats: `C E G`, `Db, F, Ab, C`, `G-B-D-F`, etc.
188
+ - Sharps/flats: `#`, `b`, or unicode `♯` `♭`.
189
+ """)
190
+ chat = gr.ChatInterface(
191
+ fn=answer,
192
+ examples=[[e] for e in example_inputs()],
193
+ title="Chord Identifier",
194
+ retry_btn=None,
195
+ undo_btn="Delete last turn",
196
+ clear_btn="Clear",
197
+ textbox=gr.Textbox(placeholder="e.g., C E G or Db, F, Ab", label="Your notes"),
198
+ )
199
+
200
+ if __name__ == "__main__":
201
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ gradio>=4.44.0