jacob-c commited on
Commit
d50db5f
·
1 Parent(s): cc2dcec
Files changed (1) hide show
  1. app.py +147 -36
app.py CHANGED
@@ -86,6 +86,33 @@ except Exception as e:
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,7 +217,7 @@ def process_audio(audio_file):
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."
@@ -242,44 +269,45 @@ ONLY WRITE THE ACTUAL LYRICS. NO EXPLANATIONS OR META-TEXT.
242
  avg_syllables = 4
243
 
244
  # Create a more direct prompt with examples and specific syllable count guidance
245
- prompt = f"""Write song lyrics for a {genre} song in {key} {mode} with tempo {tempo} BPM. The emotion is {emotion} and theme is {theme}.
246
 
247
- I need EXACTLY {num_phrases} lines of lyrics - one line for each musical phrase. Not one more, not one less.
248
 
249
- CRITICAL INSTRUCTIONS:
250
- - Each line MUST be VERY SHORT with only {min_syllables}-{max_syllables} syllables (aim for {avg_syllables} or fewer)
251
- - PRIORITIZE BREVITY - use fewer syllables rather than more
252
- - CONNECT YOUR LINES - spread complete thoughts across 2-3 consecutive lines
253
- - Create SENTENCE FLOW across lines instead of making each line independent
254
- - Let sentence clauses and phrases flow naturally across multiple lines
255
- - Each individual line should still fit into one measure of music
256
- - Use simple, short words whenever possible
257
 
258
- FORMAT:
259
- - Just write {num_phrases} plain text lines
260
- - Each line should be simple song lyrics (no annotations)
261
- - Don't include any explanations or commentary
262
- - Don't use any tags or markers
263
- - Don't include section labels like [Verse] or [Chorus]
264
-
265
- EXAMPLE OF CONNECTED THOUGHTS ACROSS LINES:
266
 
267
- Moonlight falls (3 syllables)
268
- through my window pane (5 syllables)
269
- as I wait for you (5 syllables)
 
 
270
 
271
- Notice how these 3 lines form ONE complete sentence.
 
 
272
 
273
- Another example:
274
 
275
- Whispers fade (3 syllables)
276
- in the morning light (5 syllables)
277
- leaving memories (5 syllables)
278
- of our last goodbye (5 syllables)
279
 
280
- These 4 lines form a connected thought, not independent statements.
 
 
281
 
282
- JUST THE PLAIN LYRICS, EXACTLY {num_phrases} LINES, KEEPING EACH LINE TO {min_syllables}-{max_syllables} SYLLABLES, WITH CONNECTED THOUGHTS ACROSS LINES.
283
  """
284
 
285
  # Generate lyrics using the LLM model
@@ -559,7 +587,7 @@ JUST THE PLAIN LYRICS, EXACTLY {num_phrases} LINES, KEEPING EACH LINE TO {min_sy
559
  print(error_msg)
560
  return error_msg
561
 
562
- def analyze_lyrics_rhythm_match(lyrics, lyric_templates, genre="pop"):
563
  """Analyze how well the generated lyrics match the beat patterns and syllable requirements"""
564
  if not lyric_templates or not lyrics:
565
  return "No beat templates or lyrics available for analysis."
@@ -646,12 +674,35 @@ def analyze_lyrics_rhythm_match(lyrics, lyric_templates, genre="pop"):
646
  result += f"- Average lines per thought: {sentence_flow_analysis['avg_lines_per_group']:.1f}\n"
647
  result += f"- Flow quality: {sentence_flow_analysis['flow_quality']}\n"
648
 
 
 
 
 
 
 
 
649
  # Add guidance on ideal distribution for syllables and sentence flow
650
- result += f"\n**Syllable & Flow Guidance:**\n"
651
- 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"
652
- result += f"- Break complete thoughts across 2-3 lines for natural flow\n"
653
- result += f"- Connect your lyrics with sentence fragments that flow across lines\n"
654
- result += f"- Use conjunctions, prepositions, and dependent clauses to connect lines\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
655
 
656
  # Add genre-specific notes
657
  result += f"\n**Genre Notes ({genre}):**\n"
@@ -770,6 +821,66 @@ def analyze_sentence_flow(lines):
770
  "flow_quality": flow_quality
771
  }
772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
  # Create Gradio interface
774
  def create_interface():
775
  with gr.Blocks(title="Music Analysis & Lyrics Generator") as demo:
 
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
  # 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."
 
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
 
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
  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
  "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: