RAM2118 commited on
Commit
0483495
·
verified ·
1 Parent(s): 6cd53ef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +225 -23
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import streamlit as st
2
  import uuid
3
  from harmonic_engine import (
@@ -22,11 +23,155 @@ st.markdown("""
22
  font-size: 22px; font-weight: bold; color: #f0f6fc;
23
  border-left: 4px solid #58a6ff; padding-left: 12px; margin-bottom: 8px;
24
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  </style>
26
  """, unsafe_allow_html=True)
27
 
28
  st.title("🎹 Harmonic Catalyst PRO")
29
- st.caption("Genre-Aware Voicing Engine • Multi-Section Builder • Voice Leading • Spectral Analysis")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  if 'song_sections' not in st.session_state:
32
  st.session_state.song_sections = []
@@ -79,7 +224,8 @@ with st.sidebar:
79
 
80
  input_mode = st.radio(
81
  "Input Mode",
82
- ["Chord Symbols (Cmaj7, Am7...)", "Roman Numerals (I, vi, IV, V)"]
 
83
  )
84
 
85
  if "Roman" in input_mode:
@@ -89,19 +235,40 @@ with st.sidebar:
89
  key_pc = 0
90
 
91
  st.divider()
92
- context = st.radio("🎚️ Mix Context", ["Full Band", "Solo Piano"])
 
 
 
 
93
 
94
  st.divider()
95
  st.subheader("🔧 Voice Leading")
96
- use_voice_leading = st.checkbox("🔗 Connect sections smoothly", value=True)
 
 
 
 
97
 
98
  st.divider()
99
- use_negative = st.checkbox("🌌 Negative Harmony", value=False)
 
 
 
 
100
  if use_negative:
101
  neg_key = st.selectbox("Mirror Key", NOTE_NAMES, index=0)
102
  neg_key_pc = note_name_to_pc(neg_key)
103
 
104
  st.divider()
 
 
 
 
 
 
 
 
 
105
  if st.button("🗑️ Clear All Sections"):
106
  st.session_state.song_sections = []
107
  st.session_state.section_counter = 0
@@ -161,10 +328,27 @@ def draw_piano_svg(notes, label, start_midi=36, num_keys=37):
161
 
162
  st.markdown("---")
163
  st.subheader("🎵 Song Structure Builder")
164
- st.caption("Build your song section by section. Add any section in any order!")
 
 
 
 
 
 
 
165
 
166
  if len(st.session_state.song_sections) == 0:
167
- st.info("👇 Click below to add your first section (Verse, Chorus, Intro, Bridge, etc.)")
 
 
 
 
 
 
 
 
 
 
168
  if st.button("➕ Add First Section", type="primary", use_container_width=True):
169
  add_section()
170
  st.rerun()
@@ -182,26 +366,27 @@ for idx, section in enumerate(st.session_state.song_sections):
182
  value=section['name'],
183
  key=f"name_{section['id']}",
184
  label_visibility="collapsed",
185
- placeholder="e.g., Verse 1, Chorus, Bridge, Drop..."
 
186
  )
187
 
188
  with col_up:
189
- if st.button("⬆️", key=f"up_{section['id']}", help="Move up", disabled=(idx == 0)):
190
  move_section_up(section['id'])
191
  st.rerun()
192
 
193
  with col_down:
194
- if st.button("⬇️", key=f"down_{section['id']}", help="Move down", disabled=(idx == len(st.session_state.song_sections) - 1)):
195
  move_section_down(section['id'])
196
  st.rerun()
197
 
198
  with col_dup:
199
- if st.button("📋", key=f"dup_{section['id']}", help="Duplicate"):
200
  duplicate_section(section['id'])
201
  st.rerun()
202
 
203
  with col_del:
204
- if st.button("🗑️", key=f"del_{section['id']}", help="Delete"):
205
  remove_section(section['id'])
206
  st.rerun()
207
 
@@ -209,24 +394,27 @@ for idx, section in enumerate(st.session_state.song_sections):
209
 
210
  with col1:
211
  if "Roman" in input_mode:
212
- help_text = "Roman numerals: I ii iii IV V vi vii°"
 
213
  else:
214
- help_text = "e.g., Cmaj7 Am7 Fmaj7 G7"
 
215
 
216
  section['chords'] = st.text_input(
217
  "Chord Progression",
218
  value=section['chords'],
219
  key=f"chords_{section['id']}",
220
- placeholder=help_text,
221
  help=help_text
222
  )
223
 
224
  with col2:
225
  section['genre'] = st.selectbox(
226
  "Genre/Style",
227
- ["Pop", "Jazz", "Gospel", "Blues", "Classical", "RnB", "Waltz"],
228
- index=["Pop", "Jazz", "Gospel", "Blues", "Classical", "RnB", "Waltz"].index(section['genre']),
229
- key=f"genre_{section['id']}"
 
230
  )
231
 
232
  with st.expander("⚙️ Advanced Settings", expanded=False):
@@ -235,20 +423,22 @@ for idx, section in enumerate(st.session_state.song_sections):
235
  section['lh_octave'] = st.slider(
236
  "Left Hand Octave",
237
  1, 4, section['lh_octave'],
238
- key=f"lh_{section['id']}"
 
239
  )
240
  with col_rh:
241
  section['rh_octave'] = st.slider(
242
  "Right Hand Octave",
243
  3, 6, section['rh_octave'],
244
- key=f"rh_{section['id']}"
 
245
  )
246
 
247
  st.markdown("---")
248
 
249
  col_add, col_space = st.columns([1, 3])
250
  with col_add:
251
- if st.button("➕ Add Section", use_container_width=True):
252
  add_section()
253
  st.rerun()
254
 
@@ -278,14 +468,16 @@ if st.button("🎹 Generate Full Song", type="primary", use_container_width=True
278
  if "Roman" in input_mode:
279
  root_pc, quality = roman_to_chord(token, key_pc)
280
  chord_label = f"{pc_to_note_name(root_pc)}{quality} ({token})"
 
281
  else:
282
- root_pc, quality = parse_chord_symbol(token)
283
  chord_label = token
284
 
285
  lh, rh = GenreVoicer.voice(
286
  root_pc, quality, section['genre'],
287
  octave_lh=section['lh_octave'],
288
- octave_rh=section['rh_octave']
 
289
  )
290
 
291
  if use_negative:
@@ -349,6 +541,7 @@ if st.button("🎹 Generate Full Song", type="primary", use_container_width=True
349
 
350
  if progression_data:
351
  st.subheader("💾 Export Full Song")
 
352
 
353
  try:
354
  files = MidiExporter.export(progression_data, filename_prefix="full_song")
@@ -384,3 +577,12 @@ if st.button("🎹 Generate Full Song", type="primary", use_container_width=True
384
  'Status': '✅ Clear' if not r['has_issues'] else '⚠️ Fixed'
385
  })
386
  st.dataframe(summary_data, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
1
+
2
  import streamlit as st
3
  import uuid
4
  from harmonic_engine import (
 
23
  font-size: 22px; font-weight: bold; color: #f0f6fc;
24
  border-left: 4px solid #58a6ff; padding-left: 12px; margin-bottom: 8px;
25
  }
26
+ .help-box {
27
+ background: #161b22;
28
+ border-left: 4px solid #58a6ff;
29
+ padding: 1rem;
30
+ border-radius: 8px;
31
+ margin: 1rem 0;
32
+ }
33
+ .example-box {
34
+ background: #1c2128;
35
+ border: 1px solid #30363d;
36
+ padding: 0.75rem;
37
+ border-radius: 6px;
38
+ margin: 0.5rem 0;
39
+ font-family: monospace;
40
+ }
41
  </style>
42
  """, unsafe_allow_html=True)
43
 
44
  st.title("🎹 Harmonic Catalyst PRO")
45
+ st.caption("Genre-Aware Chord Voicing Engine • Multi-Section Builder • Professional Mix Planning")
46
+
47
+ # Help/Instructions Section
48
+ with st.expander("📖 Quick Start Guide (Click to expand)", expanded=False):
49
+ st.markdown("""
50
+ ### 🎯 What This Tool Does
51
+
52
+ Transform chord progressions into **professional piano voicings** across different genres,
53
+ with smart voice leading and frequency-aware arrangement planning.
54
+
55
+ ### 🚀 How to Use
56
+
57
+ 1. **Add a Section** - Click ➕ to create verse, chorus, bridge, etc.
58
+ 2. **Enter Chords** - Type your progression (supports 20+ chord types + slash chords!)
59
+ 3. **Pick Genre** - Choose from 17 styles (Pop, Jazz, Gospel, Afrobeats, etc.)
60
+ 4. **Generate** - See piano voicings, frequency analysis, and download MIDI
61
+
62
+ ### 💡 Try These Examples
63
+ """)
64
+
65
+ col1, col2 = st.columns(2)
66
+
67
+ with col1:
68
+ st.markdown("""
69
+ **🎵 Pop Ballad:**
70
+ ```
71
+ Section 1 (Verse): C Am F G
72
+ Section 2 (Chorus): C Am F G
73
+ Genre: Pop → Gospel (for lift!)
74
+ ```
75
+
76
+ **🎷 Jazz Standard:**
77
+ ```
78
+ Chords: Dm7 G7 Cmaj7 A7
79
+ Genre: Jazz
80
+ Tip: Add slash chords for smooth bass!
81
+ ```
82
+
83
+ **🌍 Afrobeats Vibe:**
84
+ ```
85
+ Chords: Em D C Bm
86
+ Genre: Afrobeats
87
+ Tip: Try same chords in Trap for contrast
88
+ ```
89
+ """)
90
+
91
+ with col2:
92
+ st.markdown("""
93
+ **🎸 Slash Chord Magic:**
94
+ ```
95
+ C C/B Am Am/G F G/B C
96
+ Creates smooth bass line!
97
+ Genre: Any (works everywhere)
98
+ ```
99
+
100
+ **🇮🇳 Bollywood Fusion:**
101
+ ```
102
+ Section 1: Cmaj7 Am7 (Hindustani Classical)
103
+ Section 2: Fmaj7 G7 (Pop)
104
+ = Traditional intro → Modern verse
105
+ ```
106
+
107
+ **🎹 Genre Morphing:**
108
+ ```
109
+ Same chords, different sections:
110
+ Verse: Neo-Soul
111
+ Chorus: Gospel
112
+ Bridge: Jazz
113
+ ```
114
+ """)
115
+
116
+ st.markdown("""
117
+ ### 🎼 Supported Chord Types
118
+
119
+ | Type | Example | Type | Example |
120
+ |------|---------|------|---------|
121
+ | Major | `C`, `Cmaj` | Minor | `Cm`, `Cmin` |
122
+ | Major 7th | `Cmaj7` | Minor 7th | `Cm7`, `Cmin7` |
123
+ | Dominant 7th | `C7` | Suspended | `Csus2`, `Csus4` |
124
+ | Diminished | `Cdim`, `Cdim7` | Augmented | `Caug`, `C+` |
125
+ | Add 9 | `Cadd9` | 6th | `C6`, `Cm6` |
126
+ | Extended | `C9`, `C11`, `C13` | **Slash** | `C/E`, `Am/G` |
127
+
128
+ ### 🎨 All 17 Genres
129
+
130
+ **Western:** Pop • Jazz • Gospel • Blues • Classical • RnB • Waltz • Neo-Soul
131
+ **Modern:** Afrobeats • Trap • K-Pop • Lo-fi • Funk
132
+ **World:** Bossa Nova • Hindustani Classical • Reggae • Latin
133
+
134
+ ### 💡 Pro Tips
135
+
136
+ - **Use Slash Chords** for smooth bass lines: `C C/B Am Am/G`
137
+ - **Genre Morphing**: Same chords, different genres per section = dynamic arrangement
138
+ - **Voice Leading** (enabled by default): Connects sections smoothly even across genre changes
139
+ - **Frequency Analysis**: Warns if piano clashes with vocals in 160-400Hz zone
140
+ - **Duplicate Sections**: Use 📋 button to copy verse → verse 2 instantly
141
+
142
+ ### 🎯 Workflow Example: Recreating "Raabta"
143
+
144
+ 1. Section 1: "Intro" → Chords: `Cmaj7 Am7` → Genre: Hindustani Classical
145
+ 2. Section 2: "Verse" → Chords: `Cmaj7 Am7 Fmaj7 G7` → Genre: Pop
146
+ 3. Section 3: "Chorus" → Same chords → Genre: Afrobeats (for modern twist!)
147
+ 4. Generate → Download MIDI → Import to DAW → Add your sounds! 🎧
148
+ """)
149
+
150
+ with st.expander("❓ FAQ & Troubleshooting", expanded=False):
151
+ st.markdown("""
152
+ **Q: What's the difference between genres?**
153
+ A: Each genre uses different voicing techniques:
154
+ - **Jazz** = Shell voicings (root+7th in LH, 3-7-9-13 in RH)
155
+ - **Gospel** = Thick clusters with stacked 4ths and suspensions
156
+ - **Afrobeats** = Open 5ths, modern spacious sound
157
+
158
+ **Q: When should I use slash chords?**
159
+ A: When you want a specific bass note instead of the root:
160
+ - `C/E` = C major chord with E in bass (smoother transitions)
161
+ - Common in ballads, R&B, and jazz
162
+
163
+ **Q: What does "MUD WARNING" mean?**
164
+ A: Your piano has notes in 160-400Hz (where vocals sit). The tool auto-shifts them up an octave to keep your mix clean.
165
+
166
+ **Q: Can I use Roman numerals?**
167
+ A: Yes! Switch to Roman Numeral mode in sidebar, select your key, then use: `I vi IV V`
168
+
169
+ **Q: How do I save my work?**
170
+ A: Download the MIDI files, then you can reload them in your DAW anytime.
171
+
172
+ **Q: Why are voicings different even with same chords?**
173
+ A: That's the magic! Each genre has unique voicing rules. `Cmaj7` in Jazz sounds completely different from `Cmaj7` in Gospel.
174
+ """)
175
 
176
  if 'song_sections' not in st.session_state:
177
  st.session_state.song_sections = []
 
224
 
225
  input_mode = st.radio(
226
  "Input Mode",
227
+ ["Chord Symbols (Cmaj7, Am7...)", "Roman Numerals (I, vi, IV, V)"],
228
+ help="Choose how you want to enter chords. Roman numerals require selecting a key."
229
  )
230
 
231
  if "Roman" in input_mode:
 
235
  key_pc = 0
236
 
237
  st.divider()
238
+ context = st.radio(
239
+ "🎚️ Mix Context",
240
+ ["Full Band", "Solo Piano"],
241
+ help="Full Band = Strict frequency checking. Solo Piano = More freedom in mid-range."
242
+ )
243
 
244
  st.divider()
245
  st.subheader("🔧 Voice Leading")
246
+ use_voice_leading = st.checkbox(
247
+ "🔗 Connect sections smoothly",
248
+ value=True,
249
+ help="Automatically finds smoothest voicings between chords (minimal hand movement)"
250
+ )
251
 
252
  st.divider()
253
+ use_negative = st.checkbox(
254
+ "🌌 Negative Harmony",
255
+ value=False,
256
+ help="Experimental: Mirror chords around axis (Jacob Collier style)"
257
+ )
258
  if use_negative:
259
  neg_key = st.selectbox("Mirror Key", NOTE_NAMES, index=0)
260
  neg_key_pc = note_name_to_pc(neg_key)
261
 
262
  st.divider()
263
+ st.markdown("### 💡 Quick Tips")
264
+ st.info("""
265
+ **Try these now:**
266
+ - Enter: `C/E Am/G`
267
+ - Mix genres in one song
268
+ - Duplicate sections with 📋
269
+ - Download MIDI for your DAW
270
+ """)
271
+
272
  if st.button("🗑️ Clear All Sections"):
273
  st.session_state.song_sections = []
274
  st.session_state.section_counter = 0
 
328
 
329
  st.markdown("---")
330
  st.subheader("🎵 Song Structure Builder")
331
+
332
+ # Inline help text
333
+ st.markdown("""
334
+ <div class="help-box">
335
+ <strong>💡 How it works:</strong> Add sections in any order (Verse, Chorus, Bridge, etc.).
336
+ Each section can have different chords and genres. Voice leading automatically connects them smoothly!
337
+ </div>
338
+ """, unsafe_allow_html=True)
339
 
340
  if len(st.session_state.song_sections) == 0:
341
+ st.markdown("""
342
+ <div class="example-box">
343
+ <strong>🎯 Try this example:</strong><br>
344
+ 1. Click "Add First Section" below<br>
345
+ 2. Name it "Verse", enter chords: <code>Cmaj7 Am7 Fmaj7 G7</code><br>
346
+ 3. Select genre: Pop<br>
347
+ 4. Add another section with same chords, but choose "Gospel" genre<br>
348
+ 5. Click "Generate" and hear the difference!
349
+ </div>
350
+ """, unsafe_allow_html=True)
351
+
352
  if st.button("➕ Add First Section", type="primary", use_container_width=True):
353
  add_section()
354
  st.rerun()
 
366
  value=section['name'],
367
  key=f"name_{section['id']}",
368
  label_visibility="collapsed",
369
+ placeholder="e.g., Verse 1, Chorus, Bridge, Drop...",
370
+ help="Give this section a name (Verse, Chorus, etc.)"
371
  )
372
 
373
  with col_up:
374
+ if st.button("⬆️", key=f"up_{section['id']}", help="Move section up", disabled=(idx == 0)):
375
  move_section_up(section['id'])
376
  st.rerun()
377
 
378
  with col_down:
379
+ if st.button("⬇️", key=f"down_{section['id']}", help="Move section down", disabled=(idx == len(st.session_state.song_sections) - 1)):
380
  move_section_down(section['id'])
381
  st.rerun()
382
 
383
  with col_dup:
384
+ if st.button("📋", key=f"dup_{section['id']}", help="Duplicate this section"):
385
  duplicate_section(section['id'])
386
  st.rerun()
387
 
388
  with col_del:
389
+ if st.button("🗑️", key=f"del_{section['id']}", help="Delete this section"):
390
  remove_section(section['id'])
391
  st.rerun()
392
 
 
394
 
395
  with col1:
396
  if "Roman" in input_mode:
397
+ help_text = "Roman numerals: I ii iii IV V vi vii°. Add 7 for seventh chords (V7, IVmaj7)"
398
+ placeholder = "e.g., I vi IV V"
399
  else:
400
+ help_text = "Supports: maj, min, 7, maj7, sus2, sus4, add9, 9, 11, 13, dim, aug. Use / for slash chords!"
401
+ placeholder = "e.g., Cmaj7 Am7 F/A G or C C/B Am Am/G"
402
 
403
  section['chords'] = st.text_input(
404
  "Chord Progression",
405
  value=section['chords'],
406
  key=f"chords_{section['id']}",
407
+ placeholder=placeholder,
408
  help=help_text
409
  )
410
 
411
  with col2:
412
  section['genre'] = st.selectbox(
413
  "Genre/Style",
414
+ ["Pop", "Jazz", "Gospel", "Blues", "Classical", "RnB", "Waltz", "Afrobeats", "Trap", "Bossa Nova", "Hindustani Classical", "Neo-Soul", "Reggae", "Latin", "K-Pop", "Lo-fi", "Funk"],
415
+ index=["Pop", "Jazz", "Gospel", "Blues", "Classical", "RnB", "Waltz", "Afrobeats", "Trap", "Bossa Nova", "Hindustani Classical", "Neo-Soul", "Reggae", "Latin", "K-Pop", "Lo-fi", "Funk"].index(section['genre']),
416
+ key=f"genre_{section['id']}",
417
+ help="Each genre uses different professional voicing techniques"
418
  )
419
 
420
  with st.expander("⚙️ Advanced Settings", expanded=False):
 
423
  section['lh_octave'] = st.slider(
424
  "Left Hand Octave",
425
  1, 4, section['lh_octave'],
426
+ key=f"lh_{section['id']}",
427
+ help="Lower = deeper bass. Default: 2"
428
  )
429
  with col_rh:
430
  section['rh_octave'] = st.slider(
431
  "Right Hand Octave",
432
  3, 6, section['rh_octave'],
433
+ key=f"rh_{section['id']}",
434
+ help="Higher = brighter sound. Default: 4"
435
  )
436
 
437
  st.markdown("---")
438
 
439
  col_add, col_space = st.columns([1, 3])
440
  with col_add:
441
+ if st.button("➕ Add Section", use_container_width=True, help="Add another section to your song"):
442
  add_section()
443
  st.rerun()
444
 
 
468
  if "Roman" in input_mode:
469
  root_pc, quality = roman_to_chord(token, key_pc)
470
  chord_label = f"{pc_to_note_name(root_pc)}{quality} ({token})"
471
+ slash_bass = None
472
  else:
473
+ root_pc, quality, slash_bass = parse_chord_symbol(token)
474
  chord_label = token
475
 
476
  lh, rh = GenreVoicer.voice(
477
  root_pc, quality, section['genre'],
478
  octave_lh=section['lh_octave'],
479
+ octave_rh=section['rh_octave'],
480
+ slash_bass=slash_bass
481
  )
482
 
483
  if use_negative:
 
541
 
542
  if progression_data:
543
  st.subheader("💾 Export Full Song")
544
+ st.info("💡 **Tip:** Import these MIDI files into your DAW, assign sounds, and produce your track!")
545
 
546
  try:
547
  files = MidiExporter.export(progression_data, filename_prefix="full_song")
 
577
  'Status': '✅ Clear' if not r['has_issues'] else '⚠️ Fixed'
578
  })
579
  st.dataframe(summary_data, use_container_width=True)
580
+
581
+ # Footer
582
+ st.markdown("---")
583
+ st.markdown("""
584
+ <div style='text-align: center; color: #8b949e; padding: 2rem 0;'>
585
+ <p>🎹 Built for musicians, by musicians • Free & Open Source</p>
586
+ <p>Found a bug or have a suggestion? Let us know!</p>
587
+ </div>
588
+ """, unsafe_allow_html=True)