ss
Browse files
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
|
| 246 |
|
| 247 |
-
I need EXACTLY {num_phrases} lines of lyrics - one line for each musical phrase.
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 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 |
-
|
| 259 |
-
-
|
| 260 |
-
-
|
| 261 |
-
-
|
| 262 |
-
-
|
| 263 |
-
- Don't
|
| 264 |
-
|
| 265 |
-
EXAMPLE OF CONNECTED THOUGHTS ACROSS LINES:
|
| 266 |
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
|
|
|
|
|
|
| 270 |
|
| 271 |
-
|
|
|
|
|
|
|
| 272 |
|
| 273 |
-
|
| 274 |
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
of our last goodbye (5 syllables)
|
| 279 |
|
| 280 |
-
|
|
|
|
|
|
|
| 281 |
|
| 282 |
-
|
| 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**
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|