jacob-c commited on
Commit
4c87bcd
·
1 Parent(s): d50db5f
Files changed (1) hide show
  1. app.py +182 -198
app.py CHANGED
@@ -86,33 +86,6 @@ except Exception as e:
86
  # Create music analyzer instance
87
  music_analyzer = MusicAnalyzer()
88
 
89
- # Define emotion and theme lexicons for content analysis
90
- emotion_lexicons = {
91
- "happy": ["joy", "happy", "smile", "laugh", "light", "bright", "sun", "dance", "celebrate", "glow", "warm"],
92
- "sad": ["cry", "tear", "pain", "loss", "grief", "dark", "alone", "miss", "gone", "sorrow", "heart", "break"],
93
- "calm": ["peace", "quiet", "still", "gentle", "soft", "slow", "breath", "serene", "tranquil", "relax", "flow"],
94
- "energetic": ["run", "fast", "beat", "pulse", "jump", "fire", "alive", "spark", "rush", "wild", "free"],
95
- "tense": ["fear", "worry", "wait", "edge", "grip", "tight", "storm", "break", "shadow", "threat", "doubt"],
96
- "nostalgic": ["memory", "remember", "past", "time", "ago", "once", "childhood", "return", "old", "familiar", "home"],
97
- "reflective": ["think", "ponder", "wonder", "question", "search", "mind", "deep", "self", "mirror", "path", "journey"],
98
- "triumphant": ["win", "rise", "stand", "overcome", "above", "victory", "summit", "conquer", "champion", "succeed"],
99
- "yearning": ["want", "need", "desire", "reach", "seek", "dream", "hope", "wish", "long", "hunger", "thirst"],
100
- "peaceful": ["calm", "rest", "still", "quiet", "harmony", "balance", "ease", "gentle", "soft", "float", "drift"]
101
- }
102
-
103
- theme_lexicons = {
104
- "love": ["love", "heart", "touch", "together", "hold", "kiss", "embrace", "feel", "close", "intimate", "passion"],
105
- "loss": ["gone", "away", "empty", "missing", "leave", "without", "never", "forever", "lost", "memory", "shadow"],
106
- "freedom": ["free", "fly", "open", "release", "escape", "chain", "break", "boundless", "space", "breathe", "wings"],
107
- "triumph": ["victory", "overcome", "win", "rise", "mountain", "climb", "top", "struggle", "strength", "succeed"],
108
- "reflection": ["mirror", "water", "see", "self", "face", "look", "inside", "truth", "reality", "soul", "mind"],
109
- "journey": ["road", "path", "walk", "step", "travel", "distance", "far", "way", "wander", "search", "find"],
110
- "time": ["clock", "moment", "second", "hour", "pass", "wait", "forever", "instant", "eternity", "memory", "future"],
111
- "conflict": ["fight", "battle", "against", "oppose", "between", "war", "struggle", "clash", "resist", "enemy"],
112
- "nature": ["earth", "wind", "fire", "water", "sky", "tree", "flower", "mountain", "river", "ocean", "stars"],
113
- "change": ["transform", "become", "different", "shift", "turn", "evolve", "grow", "new", "begin", "end", "cycle"]
114
- }
115
-
116
  # Process uploaded audio file
117
  def process_audio(audio_file):
118
  if audio_file is None:
@@ -217,7 +190,7 @@ def process_audio(audio_file):
217
  # Generate lyrics only for supported genres
218
  if genre_supported:
219
  lyrics = generate_lyrics(music_analysis, primary_genre, duration)
220
- beat_match_analysis = analyze_lyrics_rhythm_match(lyrics, lyric_templates, primary_genre, emotion, theme)
221
  else:
222
  supported_genres_str = ", ".join([genre.capitalize() for genre in beat_analyzer.supported_genres])
223
  lyrics = f"Lyrics generation is only supported for the following genres: {supported_genres_str}.\n\nDetected genre '{primary_genre}' doesn't have strong syllable-to-beat patterns required for our lyric generation algorithm."
@@ -254,9 +227,6 @@ def generate_lyrics(music_analysis, genre, duration):
254
  ONLY WRITE THE ACTUAL LYRICS. NO EXPLANATIONS OR META-TEXT.
255
  """
256
  else:
257
- # Create phrase examples
258
- num_phrases = len(lyric_templates)
259
-
260
  # Calculate the typical syllable range for this genre
261
  if num_phrases > 0:
262
  # Get max syllables per line from templates
@@ -268,46 +238,82 @@ ONLY WRITE THE ACTUAL LYRICS. NO EXPLANATIONS OR META-TEXT.
268
  max_syllables = 7
269
  avg_syllables = 4
270
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  # Create a more direct prompt with examples and specific syllable count guidance
272
- prompt = f"""Write song lyrics for a {genre} song that truly captures the emotional essence of "{emotion}" and explores the theme of "{theme}". The song is in {key} {mode} with tempo {tempo} BPM.
273
-
274
- I need EXACTLY {num_phrases} lines of lyrics - one line for each musical phrase.
275
-
276
- YOUR TOP PRIORITIES (in order):
277
- 1. EXPRESS THE EMOTION: "{emotion}" should be felt through your word choices
278
- 2. DEVELOP THE THEME: "{theme}" should be clearly represented
279
- 3. CONNECT YOUR LINES: spread complete thoughts across 2-3 consecutive lines
280
- 4. KEEP LINES SHORT: {min_syllables}-{max_syllables} syllables per line (aim for {avg_syllables})
281
-
282
- ADDITIONAL REQUIREMENTS:
283
- - Create original lyrics that reflect this specific emotion and theme
284
- - Let sentence clauses flow naturally across line breaks
285
- - Use vivid imagery and sensory details related to the emotion
286
- - Each line should contribute to the overall theme
287
- - Don't copy the example structures - be creative and unique
288
- - Use simple, concise words that evoke strong emotions
289
-
290
- AVOID:
291
- - Generic phrases that could apply to any song
292
- - Copying patterns from the examples below
293
- - Complete, independent thoughts on each line
294
- - Abstract concepts without concrete imagery
295
 
296
  FORMAT:
297
- - Plain text, one line per musical phrase
298
- - No annotations, explanations, or labels
 
 
299
 
300
- Here's a simplified structural example of connecting thoughts across lines:
 
 
 
 
301
 
302
- Line 1 (introduces an image)
303
- Line 2 (extends the image)
304
- Line 3 (completes the thought)
305
 
306
- Line 4 (starts a new thought)
307
- Line 5 (continues it)
308
- And so on...
 
 
309
 
310
- Remember: YOUR LYRICS SHOULD DEEPLY EXPRESS "{emotion}" AND EXPLORE "{theme}" - make every word count toward these goals.
 
 
 
 
311
  """
312
 
313
  # Generate lyrics using the LLM model
@@ -491,7 +497,37 @@ Remember: YOUR LYRICS SHOULD DEEPLY EXPRESS "{emotion}" AND EXPLORE "{theme}" -
491
  # 9. Filter out any remaining empty lines after tag removal
492
  clean_lines = [line for line in clean_lines if line.strip() and not line.isspace()]
493
 
494
- # 10. If we have lyric templates, ensure we have the correct number of lines
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  if lyric_templates:
496
  num_required = len(lyric_templates)
497
 
@@ -507,76 +543,107 @@ Remember: YOUR LYRICS SHOULD DEEPLY EXPRESS "{emotion}" AND EXPLORE "{theme}" -
507
  template = lyric_templates[i]
508
  target_syllables = min(max_syllables, (template.get('min_expected', 2) + template.get('max_expected', 7)) // 2)
509
 
510
- # Create a diverse set of placeholders that match the theme/emotion
511
- placeholders = {
512
- # 2-3 syllables
 
513
  2: [
514
- "Night falls",
515
- "Time stops",
516
- "Hearts beat",
517
- "Rain falls",
518
- "Stars shine"
519
  ],
520
- # 3-4 syllables
521
  3: [
522
- "Empty chair",
523
- "Shadows dance",
524
- "Whispers fade",
525
- "Memories",
526
- "Silent room"
527
  ],
528
- # 4-5 syllables
529
  4: [
530
- "Moonlight shimmers",
531
- "Echoes of time",
532
- "Footsteps fading",
533
- "Memories drift",
534
- "Silence speaks loud"
535
  ],
536
- # 5-6 syllables
537
  5: [
538
- "Walking in the rain",
539
- "Whispers in the dark",
540
- "Echoes of your voice",
541
- "Traces left behind",
542
- "Time moves ever on"
543
  ],
544
- # 6-7 syllables
545
  6: [
546
- "Dancing in the moonlight",
547
- "Shadows play on the wall",
548
- "Memories fade to silence",
549
- "Moments lost in the wind",
550
- "Whispers of a better time"
551
  ]
552
  }
553
 
 
 
 
 
 
 
 
 
 
554
  # Get the closest matching syllable group
555
- closest_group = min(placeholders.keys(), key=lambda k: abs(k - target_syllables))
 
 
 
556
 
557
  # Choose a placeholder that hasn't been used yet
558
- available_placeholders = [p for p in placeholders[closest_group]
559
- if p not in clean_lines]
560
 
561
  if available_placeholders:
562
- placeholder = available_placeholders[i % len(available_placeholders)]
 
 
563
  else:
564
- # If we've used all placeholders in this group, create a custom one
565
- if emotion.lower() in ["sad", "nostalgic", "calm"]:
566
- placeholder = f"Memories of {emotion}"
567
- elif emotion.lower() in ["happy", "energetic"]:
568
- placeholder = f"Dancing through {emotion}"
 
 
 
 
 
 
 
 
 
569
  else:
570
- placeholder = f"Feeling {emotion} now"
 
571
  else:
572
- placeholder = "Silence speaks volumes"
573
 
574
  clean_lines.append(placeholder)
575
 
576
  # Assemble final lyrics
577
  final_lyrics = '\n'.join(clean_lines)
578
 
579
- # 11. Final sanity check - if we have nothing or garbage, return an error
 
 
 
 
 
 
 
580
  if not final_lyrics or len(final_lyrics) < 10:
581
  return "The model generated only thinking content but no actual lyrics. Please try again."
582
 
@@ -587,7 +654,7 @@ Remember: YOUR LYRICS SHOULD DEEPLY EXPRESS "{emotion}" AND EXPLORE "{theme}" -
587
  print(error_msg)
588
  return error_msg
589
 
590
- def analyze_lyrics_rhythm_match(lyrics, lyric_templates, genre="pop", emotion="reflective", theme="journey"):
591
  """Analyze how well the generated lyrics match the beat patterns and syllable requirements"""
592
  if not lyric_templates or not lyrics:
593
  return "No beat templates or lyrics available for analysis."
@@ -674,35 +741,12 @@ def analyze_lyrics_rhythm_match(lyrics, lyric_templates, genre="pop", emotion="r
674
  result += f"- Average lines per thought: {sentence_flow_analysis['avg_lines_per_group']:.1f}\n"
675
  result += f"- Flow quality: {sentence_flow_analysis['flow_quality']}\n"
676
 
677
- # Analyze theme and emotion expression
678
- content_analysis = analyze_theme_emotion_expression(lyrics, theme, emotion)
679
- result += f"\n**Theme & Emotion Expression:**\n"
680
- result += f"- Emotion ({emotion}) expression: {content_analysis['emotion_score']:.1f}% ({content_analysis['emotion_words_found']} words)\n"
681
- result += f"- Theme ({theme}) development: {content_analysis['theme_score']:.1f}% ({content_analysis['theme_words_found']} words)\n"
682
- result += f"- Overall expression quality: {content_analysis['expression_quality']}\n"
683
-
684
  # Add guidance on ideal distribution for syllables and sentence flow
685
- result += f"\n**Improvement Recommendations:**\n"
686
-
687
- # Syllable recommendations
688
- if range_match_rate < 70:
689
- result += f"- **Syllable count:** Aim for {min([t.get('min_expected', 3) for t in lyric_templates])}-{max([t.get('max_expected', 7) for t in lyric_templates])} syllables per line\n"
690
-
691
- # Flow recommendations
692
- if sentence_flow_analysis['connected_groups'] < len(lines) / 5:
693
- result += f"- **Line connections:** Break complete thoughts across 2-3 lines using conjunctions and prepositions\n"
694
- result += f"- **Flow techniques:** Start lines with connecting words like 'as', 'when', 'while', 'through'\n"
695
-
696
- # Theme/emotion recommendations
697
- if content_analysis['emotion_score'] < 20:
698
- result += f"- **Emotion expression:** Use more words that evoke '{emotion}' feelings (e.g., {', '.join(emotion_lexicons.get(emotion.lower(), ['expressive words'])[:3])})\n"
699
-
700
- if content_analysis['theme_score'] < 20:
701
- result += f"- **Theme development:** Incorporate more '{theme}' imagery and concepts (e.g., {', '.join(theme_lexicons.get(theme.lower(), ['thematic words'])[:3])})\n"
702
-
703
- # General recommendations
704
- result += f"- **Originality:** Avoid generic phrases and create specific, vivid imagery\n"
705
- result += f"- **Sensory details:** Include concrete details that can be seen, heard, or felt\n"
706
 
707
  # Add genre-specific notes
708
  result += f"\n**Genre Notes ({genre}):**\n"
@@ -821,66 +865,6 @@ def analyze_sentence_flow(lines):
821
  "flow_quality": flow_quality
822
  }
823
 
824
- def analyze_theme_emotion_expression(lyrics, theme, emotion):
825
- """Analyze how well the lyrics express the target theme and emotion"""
826
-
827
- # Normalize inputs
828
- lyrics_text = lyrics.lower()
829
- emotion = emotion.lower()
830
- theme = theme.lower()
831
-
832
- # Find closest emotion lexicon if exact match not found
833
- if emotion not in emotion_lexicons:
834
- closest_emotion = "reflective" # Default
835
- for key in emotion_lexicons:
836
- if emotion in key or key in emotion:
837
- closest_emotion = key
838
- break
839
- emotion = closest_emotion
840
-
841
- # Find closest theme lexicon if exact match not found
842
- if theme not in theme_lexicons:
843
- closest_theme = "journey" # Default
844
- for key in theme_lexicons:
845
- if theme in key or key in theme:
846
- closest_theme = key
847
- break
848
- theme = closest_theme
849
-
850
- # Count emotional and thematic words in lyrics
851
- emotion_matches = 0
852
- theme_matches = 0
853
-
854
- for word in emotion_lexicons[emotion]:
855
- if word in lyrics_text:
856
- emotion_matches += 1
857
-
858
- for word in theme_lexicons[theme]:
859
- if word in lyrics_text:
860
- theme_matches += 1
861
-
862
- # Calculate scores as percentages of available words
863
- emotion_score = min(100, (emotion_matches / len(emotion_lexicons[emotion])) * 100)
864
- theme_score = min(100, (theme_matches / len(theme_lexicons[theme])) * 100)
865
-
866
- # Qualitative assessment
867
- if emotion_score >= 30 and theme_score >= 30:
868
- expression_quality = "Strong"
869
- elif emotion_score >= 20 and theme_score >= 20:
870
- expression_quality = "Good"
871
- elif emotion_score >= 10 and theme_score >= 10:
872
- expression_quality = "Fair"
873
- else:
874
- expression_quality = "Weak"
875
-
876
- return {
877
- "emotion_score": emotion_score,
878
- "theme_score": theme_score,
879
- "expression_quality": expression_quality,
880
- "emotion_words_found": emotion_matches,
881
- "theme_words_found": theme_matches
882
- }
883
-
884
  # Create Gradio interface
885
  def create_interface():
886
  with gr.Blocks(title="Music Analysis & Lyrics Generator") as demo:
 
86
  # Create music analyzer instance
87
  music_analyzer = MusicAnalyzer()
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  # Process uploaded audio file
90
  def process_audio(audio_file):
91
  if audio_file is None:
 
190
  # Generate lyrics only for supported genres
191
  if genre_supported:
192
  lyrics = generate_lyrics(music_analysis, primary_genre, duration)
193
+ beat_match_analysis = analyze_lyrics_rhythm_match(lyrics, lyric_templates, primary_genre)
194
  else:
195
  supported_genres_str = ", ".join([genre.capitalize() for genre in beat_analyzer.supported_genres])
196
  lyrics = f"Lyrics generation is only supported for the following genres: {supported_genres_str}.\n\nDetected genre '{primary_genre}' doesn't have strong syllable-to-beat patterns required for our lyric generation algorithm."
 
227
  ONLY WRITE THE ACTUAL LYRICS. NO EXPLANATIONS OR META-TEXT.
228
  """
229
  else:
 
 
 
230
  # Calculate the typical syllable range for this genre
231
  if num_phrases > 0:
232
  # Get max syllables per line from templates
 
238
  max_syllables = 7
239
  avg_syllables = 4
240
 
241
+ # Create random examples based on the song's theme and emotion
242
+ # to avoid the LLM copying our examples directly
243
+ example_themes = [
244
+ {"emotion": "love", "fragments": ["I see your face", "across the room", "my heart beats fast", "can't look away"]},
245
+ {"emotion": "sadness", "fragments": ["tears fall like rain", "on empty streets", "memories fade", "into the dark"]},
246
+ {"emotion": "nostalgia", "fragments": ["old photographs", "dusty and worn", "remind me of when", "we were young"]},
247
+ {"emotion": "hope", "fragments": ["dawn breaks through clouds", "new day begins", "darkness recedes", "light fills my soul"]},
248
+ {"emotion": "longing", "fragments": ["miles apart now", "under same stars", "thinking of you", "across the distance"]}
249
+ ]
250
+
251
+ # Select a theme that doesn't match the song's emotion to avoid copying
252
+ selected_themes = [t for t in example_themes if t["emotion"].lower() != emotion.lower()]
253
+ if not selected_themes:
254
+ selected_themes = example_themes
255
+
256
+ import random
257
+ example_theme = random.choice(selected_themes)
258
+ example_fragments = example_theme["fragments"]
259
+ random.shuffle(example_fragments) # Randomize order
260
+
261
+ # Create example 1 - grammatical connection with conjunction
262
+ ex1_line1 = example_fragments[0] if len(example_fragments) > 0 else "The morning sun"
263
+ ex1_line2 = example_fragments[1] if len(example_fragments) > 1 else "breaks through clouds"
264
+ ex1_line3 = example_fragments[2] if len(example_fragments) > 2 else "as birds begin"
265
+ ex1_line4 = example_fragments[3] if len(example_fragments) > 3 else "their dawn chorus"
266
+
267
+ # Create example 2 - prepositional connection
268
+ ex2_fragments = [
269
+ "She walks alone",
270
+ "through crowded streets",
271
+ "with memories",
272
+ "of better days"
273
+ ]
274
+ random.shuffle(ex2_fragments)
275
+
276
  # Create a more direct prompt with examples and specific syllable count guidance
277
+ prompt = f"""Write song lyrics for a {genre} song in {key} {mode} with tempo {tempo} BPM.
278
+
279
+ PRIMARY THEME: {theme}
280
+ EMOTION: {emotion}
281
+
282
+ I need EXACTLY {num_phrases} lines of lyrics with these requirements:
283
+
284
+ KEY INSTRUCTIONS:
285
+ 1. CONNECTED THOUGHTS: Spread complete sentences across multiple lines (2-3 lines)
286
+ 2. SHORT LINES: Keep each line between {min_syllables}-{max_syllables} syllables
287
+ 3. NATURAL FLOW: Use conjunctions, prepositions and fragments that flow together
288
+ 4. AVOID CLICHÉS: Don't use phrases like "moonlight shimmers" or "time slips away"
289
+ 5. ORIGINALITY: Create fresh, original imagery related to {theme} and {emotion}
290
+ 6. DISTINCTIVE VOICE: Use specific, concrete details rather than generic phrases
 
 
 
 
 
 
 
 
 
291
 
292
  FORMAT:
293
+ - Write exactly {num_phrases} plain text lines of lyrics (just the lyrics, no annotations)
294
+ - Don't include explanations, section markers, or line numbers
295
+
296
+ ===== EXAMPLES OF CONNECTED THOUGHTS =====
297
 
298
+ Example 1 - Connection with conjunction:
299
+ {ex1_line1}
300
+ {ex1_line2}
301
+ {ex1_line3}
302
+ {ex1_line4}
303
 
304
+ Notice how these lines connect grammatically to form complete thoughts.
 
 
305
 
306
+ Example 2 - Connection with prepositions:
307
+ {ex2_fragments[0]}
308
+ {ex2_fragments[1]}
309
+ {ex2_fragments[2]}
310
+ {ex2_fragments[3]}
311
 
312
+ These lines use prepositions to create flow across multiple lines.
313
+
314
+ IMPORTANT: DO NOT copy my examples or use their wording. Create ENTIRELY NEW, ORIGINAL lyrics about {theme} with {emotion} feeling that have your own distinctive voice and perspective.
315
+
316
+ Just write {num_phrases} lines of original lyrics, focusing on connecting your lines with thought flow.
317
  """
318
 
319
  # Generate lyrics using the LLM model
 
497
  # 9. Filter out any remaining empty lines after tag removal
498
  clean_lines = [line for line in clean_lines if line.strip() and not line.isspace()]
499
 
500
+ # 10. NEW: Check for template copying or clichéd phrases
501
+ cliched_patterns = [
502
+ r'moonlight (shimmers?|falls?|dances?)',
503
+ r'shadows? (dance|play|fall|stretch)',
504
+ r'time slips? away',
505
+ r'whispers? (fade|in the)',
506
+ r'silence speaks',
507
+ r'stars? shine',
508
+ r'hearts? beat',
509
+ r'footsteps (fade|echo)',
510
+ r'gentle wind',
511
+ r'(old|empty) (roads?|chair)',
512
+ r'night (holds?|falls?)',
513
+ r'memories fade',
514
+ r'dreams (linger|drift)'
515
+ ]
516
+
517
+ cliche_count = 0
518
+ for line in clean_lines:
519
+ for pattern in cliched_patterns:
520
+ if re.search(pattern, line.lower()):
521
+ cliche_count += 1
522
+ break
523
+
524
+ # Calculate percentage of clichéd lines
525
+ if clean_lines:
526
+ cliche_percentage = (cliche_count / len(clean_lines)) * 100
527
+ else:
528
+ cliche_percentage = 0
529
+
530
+ # 11. If we have lyric templates, ensure we have the correct number of lines
531
  if lyric_templates:
532
  num_required = len(lyric_templates)
533
 
 
543
  template = lyric_templates[i]
544
  target_syllables = min(max_syllables, (template.get('min_expected', 2) + template.get('max_expected', 7)) // 2)
545
 
546
+ # Generate more creative, contextual placeholders with specificity
547
+ # Avoid clichés like "moonlight shimmers" or "time slips away"
548
+ specific_placeholders = {
549
+ # 2-3 syllables - specific, concrete phrases
550
  2: [
551
+ "Phone rings twice",
552
+ "Dogs bark loud",
553
+ "Keys dropped here",
554
+ "Train rolls by",
555
+ "Birds take flight"
556
  ],
557
+ # 3-4 syllables - specific contexts
558
  3: [
559
+ "Coffee's getting cold",
560
+ "Fan blades spinning",
561
+ "Pages turning slow",
562
+ "Neighbors argue loud",
563
+ "Radio plays soft"
564
  ],
565
+ # 4-5 syllables - specific details
566
  4: [
567
+ "Fingers tap the table",
568
+ "Taxi waits in rain",
569
+ "Laptop screen flickers",
570
+ "Wedding ring forgotten",
571
+ "Sunday church bells ring"
572
  ],
573
+ # 5-6 syllables - context rich
574
  5: [
575
+ "Letters sent without stamps",
576
+ "Broken watch shows wrong time",
577
+ "Faded jeans with torn knees",
578
+ "Neighbor's dog keeps barking",
579
+ "Smoke detector beeping"
580
  ],
581
+ # 6-7 syllables - specific scenarios
582
  6: [
583
+ "Fingerprints on dusty frames",
584
+ "Afternoon bus running late",
585
+ "Yellowed photos in a box",
586
+ "Backyard swing rocks in the wind",
587
+ "Curtains move with summer breeze"
588
  ]
589
  }
590
 
591
+ # Make theme and emotion specific placeholders to add to the list
592
+ theme_specific = []
593
+ if theme.lower() in ["love", "relationship", "romance"]:
594
+ theme_specific = ["Lipstick on a glass", "Texts left on read", "Perfume on your coat"]
595
+ elif theme.lower() in ["loss", "grief", "sadness"]:
596
+ theme_specific = ["Empty chair remains", "Photos face-down now", "Clothes still in the closet"]
597
+ elif theme.lower() in ["hope", "inspiration", "triumph"]:
598
+ theme_specific = ["Seeds begin to sprout", "Finish line in sight", "Mountain peak awaits"]
599
+
600
  # Get the closest matching syllable group
601
+ closest_group = min(specific_placeholders.keys(), key=lambda k: abs(k - target_syllables))
602
+
603
+ # Create pool of available placeholders from both specific and theme specific options
604
+ all_placeholders = specific_placeholders[closest_group] + theme_specific
605
 
606
  # Choose a placeholder that hasn't been used yet
607
+ available_placeholders = [p for p in all_placeholders if p not in clean_lines]
 
608
 
609
  if available_placeholders:
610
+ # Use modulo for more variation
611
+ idx = (i * 17 + len(clean_lines) * 13) % len(available_placeholders)
612
+ placeholder = available_placeholders[idx]
613
  else:
614
+ # If we've used all placeholders, create something random and specific
615
+ subjects = ["Car", "Dog", "Child", "Clock", "Phone", "Tree", "Book", "Door", "Light"]
616
+ verbs = ["waits", "moves", "stops", "falls", "breaks", "turns", "opens", "runs", "sleeps"]
617
+ modifiers = ["slowly", "loudly", "brightly", "quickly", "gently", "badly", "forever", "never", "always"]
618
+
619
+ # Ensure randomness with seed that changes with each call
620
+ import random
621
+ random.seed(len(clean_lines) * 27 + i * 31)
622
+
623
+ subj = random.choice(subjects)
624
+ verb = random.choice(verbs)
625
+
626
+ if target_syllables <= 3:
627
+ placeholder = f"{subj} {verb}"
628
  else:
629
+ mod = random.choice(modifiers)
630
+ placeholder = f"{subj} {verb} {mod}"
631
  else:
632
+ placeholder = "Final page turns now"
633
 
634
  clean_lines.append(placeholder)
635
 
636
  # Assemble final lyrics
637
  final_lyrics = '\n'.join(clean_lines)
638
 
639
+ # Add a warning if we detected too many clichés
640
+ if cliche_percentage >= 40:
641
+ final_lyrics = f"""WARNING: These lyrics contain several overused phrases and clichés.
642
+ Try regenerating for more original content.
643
+
644
+ {final_lyrics}"""
645
+
646
+ # 12. Final sanity check - if we have nothing or garbage, return an error
647
  if not final_lyrics or len(final_lyrics) < 10:
648
  return "The model generated only thinking content but no actual lyrics. Please try again."
649
 
 
654
  print(error_msg)
655
  return error_msg
656
 
657
+ def analyze_lyrics_rhythm_match(lyrics, lyric_templates, genre="pop"):
658
  """Analyze how well the generated lyrics match the beat patterns and syllable requirements"""
659
  if not lyric_templates or not lyrics:
660
  return "No beat templates or lyrics available for analysis."
 
741
  result += f"- Average lines per thought: {sentence_flow_analysis['avg_lines_per_group']:.1f}\n"
742
  result += f"- Flow quality: {sentence_flow_analysis['flow_quality']}\n"
743
 
 
 
 
 
 
 
 
744
  # Add guidance on ideal distribution for syllables and sentence flow
745
+ result += f"\n**Syllable & Flow Guidance:**\n"
746
+ result += f"- Aim for {min([t.get('min_expected', 3) for t in lyric_templates])}-{max([t.get('max_expected', 7) for t in lyric_templates])} syllables per line\n"
747
+ result += f"- Break complete thoughts across 2-3 lines for natural flow\n"
748
+ result += f"- Connect your lyrics with sentence fragments that flow across lines\n"
749
+ result += f"- Use conjunctions, prepositions, and dependent clauses to connect lines\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
750
 
751
  # Add genre-specific notes
752
  result += f"\n**Genre Notes ({genre}):**\n"
 
865
  "flow_quality": flow_quality
866
  }
867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
868
  # Create Gradio interface
869
  def create_interface():
870
  with gr.Blocks(title="Music Analysis & Lyrics Generator") as demo: