ss
Browse files- app.py +109 -66
- beat_analysis.py +12 -12
app.py
CHANGED
@@ -282,41 +282,42 @@ ONLY WRITE THE ACTUAL LYRICS. NO EXPLANATIONS OR META-TEXT.
|
|
282 |
PRIMARY THEME: {theme}
|
283 |
EMOTION: {emotion}
|
284 |
|
285 |
-
I need EXACTLY {num_phrases} lines of lyrics with these requirements:
|
286 |
-
|
287 |
-
|
288 |
-
1.
|
289 |
-
2.
|
290 |
-
3.
|
291 |
-
4.
|
292 |
-
5.
|
293 |
-
6.
|
|
|
|
|
294 |
|
295 |
FORMAT:
|
296 |
-
- Write exactly {num_phrases}
|
297 |
-
-
|
298 |
-
|
299 |
-
===== EXAMPLES OF CONNECTED THOUGHTS =====
|
300 |
|
301 |
-
|
302 |
-
{ex1_line1}
|
303 |
-
{ex1_line2}
|
304 |
-
{ex1_line3}
|
305 |
-
{ex1_line4}
|
306 |
|
307 |
-
|
308 |
|
309 |
-
Example
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
|
315 |
-
|
|
|
|
|
|
|
|
|
316 |
|
317 |
-
|
318 |
|
319 |
-
|
320 |
"""
|
321 |
|
322 |
# Generate lyrics using the LLM model
|
@@ -500,7 +501,16 @@ Just write {num_phrases} lines of original lyrics, focusing on connecting your l
|
|
500 |
# 9. Filter out any remaining empty lines after tag removal
|
501 |
clean_lines = [line for line in clean_lines if line.strip() and not line.isspace()]
|
502 |
|
503 |
-
# 10. NEW:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
504 |
cliched_patterns = [
|
505 |
r'moonlight (shimmers?|falls?|dances?)',
|
506 |
r'shadows? (dance|play|fall|stretch)',
|
@@ -530,7 +540,7 @@ Just write {num_phrases} lines of original lyrics, focusing on connecting your l
|
|
530 |
else:
|
531 |
cliche_percentage = 0
|
532 |
|
533 |
-
#
|
534 |
if lyric_templates:
|
535 |
num_required = len(lyric_templates)
|
536 |
|
@@ -544,7 +554,7 @@ Just write {num_phrases} lines of original lyrics, focusing on connecting your l
|
|
544 |
i = len(clean_lines)
|
545 |
if i < len(lyric_templates):
|
546 |
template = lyric_templates[i]
|
547 |
-
target_syllables = min(
|
548 |
|
549 |
# Generate more creative, contextual placeholders with specificity
|
550 |
# Avoid clichés like "moonlight shimmers" or "time slips away"
|
@@ -559,46 +569,38 @@ Just write {num_phrases} lines of original lyrics, focusing on connecting your l
|
|
559 |
],
|
560 |
# 3-4 syllables - specific contexts
|
561 |
3: [
|
562 |
-
"Coffee
|
563 |
-
"Fan blades
|
564 |
-
"Pages
|
565 |
-
"Neighbors
|
566 |
-
"Radio
|
567 |
],
|
568 |
# 4-5 syllables - specific details
|
569 |
4: [
|
570 |
-
"Fingers tap
|
571 |
"Taxi waits in rain",
|
572 |
-
"Laptop screen
|
573 |
-
"
|
574 |
-
"
|
575 |
],
|
576 |
# 5-6 syllables - context rich
|
577 |
5: [
|
578 |
-
"Letters
|
579 |
-
"
|
580 |
-
"
|
581 |
-
"
|
582 |
-
"Smoke
|
583 |
-
],
|
584 |
-
# 6-7 syllables - specific scenarios
|
585 |
-
6: [
|
586 |
-
"Fingerprints on dusty frames",
|
587 |
-
"Afternoon bus running late",
|
588 |
-
"Yellowed photos in a box",
|
589 |
-
"Backyard swing rocks in the wind",
|
590 |
-
"Curtains move with summer breeze"
|
591 |
]
|
592 |
}
|
593 |
|
594 |
# Make theme and emotion specific placeholders to add to the list
|
595 |
theme_specific = []
|
596 |
if theme.lower() in ["love", "relationship", "romance"]:
|
597 |
-
theme_specific = ["Lipstick on
|
598 |
elif theme.lower() in ["loss", "grief", "sadness"]:
|
599 |
-
theme_specific = ["
|
600 |
elif theme.lower() in ["hope", "inspiration", "triumph"]:
|
601 |
-
theme_specific = ["Seeds
|
602 |
|
603 |
# Get the closest matching syllable group
|
604 |
closest_group = min(specific_placeholders.keys(), key=lambda k: abs(k - target_syllables))
|
@@ -615,9 +617,8 @@ Just write {num_phrases} lines of original lyrics, focusing on connecting your l
|
|
615 |
placeholder = available_placeholders[idx]
|
616 |
else:
|
617 |
# If we've used all placeholders, create something random and specific
|
618 |
-
subjects = ["Car", "Dog", "
|
619 |
-
verbs = ["waits", "moves", "stops", "falls", "breaks", "turns", "
|
620 |
-
modifiers = ["slowly", "loudly", "brightly", "quickly", "gently", "badly", "forever", "never", "always"]
|
621 |
|
622 |
# Ensure randomness with seed that changes with each call
|
623 |
import random
|
@@ -626,13 +627,9 @@ Just write {num_phrases} lines of original lyrics, focusing on connecting your l
|
|
626 |
subj = random.choice(subjects)
|
627 |
verb = random.choice(verbs)
|
628 |
|
629 |
-
|
630 |
-
placeholder = f"{subj} {verb}"
|
631 |
-
else:
|
632 |
-
mod = random.choice(modifiers)
|
633 |
-
placeholder = f"{subj} {verb} {mod}"
|
634 |
else:
|
635 |
-
placeholder = "
|
636 |
|
637 |
clean_lines.append(placeholder)
|
638 |
|
@@ -645,8 +642,8 @@ Just write {num_phrases} lines of original lyrics, focusing on connecting your l
|
|
645 |
Try regenerating for more original content.
|
646 |
|
647 |
{final_lyrics}"""
|
648 |
-
|
649 |
-
#
|
650 |
if not final_lyrics or len(final_lyrics) < 10:
|
651 |
return "The model generated only thinking content but no actual lyrics. Please try again."
|
652 |
|
@@ -868,6 +865,52 @@ def analyze_sentence_flow(lines):
|
|
868 |
"flow_quality": flow_quality
|
869 |
}
|
870 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
871 |
# Create Gradio interface
|
872 |
def create_interface():
|
873 |
with gr.Blocks(title="Music Analysis & Lyrics Generator") as demo:
|
|
|
282 |
PRIMARY THEME: {theme}
|
283 |
EMOTION: {emotion}
|
284 |
|
285 |
+
I need EXACTLY {num_phrases} lines of lyrics with these STRICT requirements:
|
286 |
+
|
287 |
+
CRITICAL INSTRUCTIONS:
|
288 |
+
1. EXTREMELY SHORT LINES: Each line MUST be between {min_syllables}-{max_syllables} syllables MAXIMUM
|
289 |
+
2. ENFORCE BREVITY: NO exceptions to the syllable limit - not a single line should exceed {max_syllables} syllables
|
290 |
+
3. FRAGMENT STYLE: Use sentence fragments and short phrases instead of complete sentences
|
291 |
+
4. CONNECTED THOUGHTS: Use prepositions and conjunctions at the start of lines to connect ideas
|
292 |
+
5. SIMPLE WORDS: Choose one or two-syllable words whenever possible
|
293 |
+
6. CONCRETE IMAGERY: Use specific, tangible details rather than abstract concepts
|
294 |
+
7. NO CLICHÉS: Avoid common phrases like "time slips away" or "memories fade"
|
295 |
+
8. ONE THOUGHT PER LINE: Express just one simple idea in each line
|
296 |
|
297 |
FORMAT:
|
298 |
+
- Write exactly {num_phrases} short text lines
|
299 |
+
- No annotations, explanations, or line numbers
|
300 |
+
- Do not count syllables in the output
|
|
|
301 |
|
302 |
+
IMPORTANT: If you can't express an idea in {max_syllables} or fewer syllables, break it across two lines or choose a simpler way to express it.
|
|
|
|
|
|
|
|
|
303 |
|
304 |
+
===== EXAMPLES OF CORRECT LENGTH =====
|
305 |
|
306 |
+
Example 1 (short fragments connected by flow):
|
307 |
+
Cold tea cup (3 syllables)
|
308 |
+
on windowsill (3 syllables)
|
309 |
+
cat watches rain (3 syllables)
|
310 |
+
through foggy glass (3 syllables)
|
311 |
|
312 |
+
Example 2 (prepositional connections):
|
313 |
+
Keys dropped here (3 syllables)
|
314 |
+
by the front door (3 syllables)
|
315 |
+
where shoes pile up (3 syllables)
|
316 |
+
since you moved in (3 syllables)
|
317 |
|
318 |
+
DO NOT copy my examples. Create ENTIRELY NEW lyrics about {theme} with {emotion} feeling.
|
319 |
|
320 |
+
REMEMBER: NO LINE SHOULD EXCEED {max_syllables} SYLLABLES - this is the most important rule!
|
321 |
"""
|
322 |
|
323 |
# Generate lyrics using the LLM model
|
|
|
501 |
# 9. Filter out any remaining empty lines after tag removal
|
502 |
clean_lines = [line for line in clean_lines if line.strip() and not line.isspace()]
|
503 |
|
504 |
+
# 10. NEW: Apply strict syllable enforcement - split or truncate lines that are too long
|
505 |
+
# This is a critical step to ensure no line exceeds our max syllable count
|
506 |
+
if lyric_templates:
|
507 |
+
max_allowed_syllables = min(7, max([t.get('max_expected', 6) for t in lyric_templates]))
|
508 |
+
else:
|
509 |
+
max_allowed_syllables = 6
|
510 |
+
|
511 |
+
clean_lines = enforce_syllable_limits(clean_lines, max_allowed_syllables)
|
512 |
+
|
513 |
+
# 11. NEW: Check for template copying or clichéd phrases
|
514 |
cliched_patterns = [
|
515 |
r'moonlight (shimmers?|falls?|dances?)',
|
516 |
r'shadows? (dance|play|fall|stretch)',
|
|
|
540 |
else:
|
541 |
cliche_percentage = 0
|
542 |
|
543 |
+
# 12. If we have lyric templates, ensure we have the correct number of lines
|
544 |
if lyric_templates:
|
545 |
num_required = len(lyric_templates)
|
546 |
|
|
|
554 |
i = len(clean_lines)
|
555 |
if i < len(lyric_templates):
|
556 |
template = lyric_templates[i]
|
557 |
+
target_syllables = min(max_allowed_syllables - 1, (template.get('min_expected', 2) + template.get('max_expected', 6)) // 2)
|
558 |
|
559 |
# Generate more creative, contextual placeholders with specificity
|
560 |
# Avoid clichés like "moonlight shimmers" or "time slips away"
|
|
|
569 |
],
|
570 |
# 3-4 syllables - specific contexts
|
571 |
3: [
|
572 |
+
"Coffee gets cold",
|
573 |
+
"Fan blades spin",
|
574 |
+
"Pages turn slow",
|
575 |
+
"Neighbors talk",
|
576 |
+
"Radio hums soft"
|
577 |
],
|
578 |
# 4-5 syllables - specific details
|
579 |
4: [
|
580 |
+
"Fingers tap table",
|
581 |
"Taxi waits in rain",
|
582 |
+
"Laptop screen blinks",
|
583 |
+
"Ring left on sink",
|
584 |
+
"Church bells ring loud"
|
585 |
],
|
586 |
# 5-6 syllables - context rich
|
587 |
5: [
|
588 |
+
"Letters with no stamps",
|
589 |
+
"Watch shows wrong time",
|
590 |
+
"Jeans with torn knees",
|
591 |
+
"Dog barks next door",
|
592 |
+
"Smoke alarm beeps"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
593 |
]
|
594 |
}
|
595 |
|
596 |
# Make theme and emotion specific placeholders to add to the list
|
597 |
theme_specific = []
|
598 |
if theme.lower() in ["love", "relationship", "romance"]:
|
599 |
+
theme_specific = ["Lipstick on glass", "Text left on read", "Scent on your coat"]
|
600 |
elif theme.lower() in ["loss", "grief", "sadness"]:
|
601 |
+
theme_specific = ["Chair sits empty", "Photos face down", "Clothes in closet"]
|
602 |
elif theme.lower() in ["hope", "inspiration", "triumph"]:
|
603 |
+
theme_specific = ["Seeds start to grow", "Finish line waits", "New day breaks through"]
|
604 |
|
605 |
# Get the closest matching syllable group
|
606 |
closest_group = min(specific_placeholders.keys(), key=lambda k: abs(k - target_syllables))
|
|
|
617 |
placeholder = available_placeholders[idx]
|
618 |
else:
|
619 |
# If we've used all placeholders, create something random and specific
|
620 |
+
subjects = ["Car", "Dog", "Kid", "Clock", "Phone", "Tree", "Book", "Door", "Light"]
|
621 |
+
verbs = ["waits", "moves", "stops", "falls", "breaks", "turns", "sleeps"]
|
|
|
622 |
|
623 |
# Ensure randomness with seed that changes with each call
|
624 |
import random
|
|
|
627 |
subj = random.choice(subjects)
|
628 |
verb = random.choice(verbs)
|
629 |
|
630 |
+
placeholder = f"{subj} {verb}"
|
|
|
|
|
|
|
|
|
631 |
else:
|
632 |
+
placeholder = "Page turns slow"
|
633 |
|
634 |
clean_lines.append(placeholder)
|
635 |
|
|
|
642 |
Try regenerating for more original content.
|
643 |
|
644 |
{final_lyrics}"""
|
645 |
+
|
646 |
+
# 13. 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 |
|
|
|
865 |
"flow_quality": flow_quality
|
866 |
}
|
867 |
|
868 |
+
def enforce_syllable_limits(lines, max_syllables=6):
|
869 |
+
"""
|
870 |
+
Enforce syllable limits by splitting or truncating lines that are too long.
|
871 |
+
Returns a modified list of lines where no line exceeds max_syllables.
|
872 |
+
"""
|
873 |
+
if not lines:
|
874 |
+
return []
|
875 |
+
|
876 |
+
result_lines = []
|
877 |
+
|
878 |
+
for line in lines:
|
879 |
+
words = line.split()
|
880 |
+
if not words:
|
881 |
+
continue
|
882 |
+
|
883 |
+
# Count syllables in the line
|
884 |
+
syllable_count = sum(count_syllables_for_word(word) for word in words)
|
885 |
+
|
886 |
+
# If within limits, keep the line as is
|
887 |
+
if syllable_count <= max_syllables:
|
888 |
+
result_lines.append(line)
|
889 |
+
continue
|
890 |
+
|
891 |
+
# Line is too long - we need to split or truncate it
|
892 |
+
current_line = []
|
893 |
+
current_syllables = 0
|
894 |
+
|
895 |
+
for word in words:
|
896 |
+
word_syllables = count_syllables_for_word(word)
|
897 |
+
|
898 |
+
# If adding this word would exceed the limit, start a new line
|
899 |
+
if current_syllables + word_syllables > max_syllables and current_line:
|
900 |
+
result_lines.append(" ".join(current_line))
|
901 |
+
current_line = [word]
|
902 |
+
current_syllables = word_syllables
|
903 |
+
else:
|
904 |
+
# Add the word to the current line
|
905 |
+
current_line.append(word)
|
906 |
+
current_syllables += word_syllables
|
907 |
+
|
908 |
+
# Don't forget the last line if there are words left
|
909 |
+
if current_line:
|
910 |
+
result_lines.append(" ".join(current_line))
|
911 |
+
|
912 |
+
return result_lines
|
913 |
+
|
914 |
# Create Gradio interface
|
915 |
def create_interface():
|
916 |
with gr.Blocks(title="Music Analysis & Lyrics Generator") as demo:
|
beat_analysis.py
CHANGED
@@ -276,16 +276,16 @@ class BeatAnalyzer:
|
|
276 |
visual_pattern += "weak "
|
277 |
|
278 |
# Estimate number of words based on beats (very rough estimate)
|
279 |
-
est_words = max(1, int(num_beats * 0.
|
280 |
|
281 |
-
# Estimate syllables - use
|
282 |
-
# For 4/4 time signature, we want to
|
283 |
if stress_pattern == "SWMW": # 4/4 time
|
284 |
-
min_syllables = max(1, int(num_beats * 0.
|
285 |
-
max_syllables = min(
|
286 |
else:
|
287 |
-
min_syllables = max(1, int(num_beats * 0.
|
288 |
-
max_syllables = min(
|
289 |
|
290 |
# Store these in the template for future reference
|
291 |
template['min_expected'] = min_syllables
|
@@ -294,7 +294,7 @@ class BeatAnalyzer:
|
|
294 |
guide = f"~{est_words} words, ~{min_syllables}-{max_syllables} syllables | Pattern: {visual_pattern}"
|
295 |
|
296 |
# Add additional guidance to the template for natural phrasing
|
297 |
-
template['phrasing_guide'] = "
|
298 |
|
299 |
return guide
|
300 |
|
@@ -315,13 +315,13 @@ class BeatAnalyzer:
|
|
315 |
min_ratio, typical_ratio, max_ratio = self.genre_syllable_ratios['default']
|
316 |
|
317 |
# Calculate flexible min and max syllable expectations based on genre
|
318 |
-
# Use
|
319 |
min_expected = max(1, int(expected_count * min_ratio))
|
320 |
-
max_expected = min(
|
321 |
|
322 |
-
# For 4/4 time signature, cap the max syllables per line
|
323 |
if template['stress_pattern'] == "SWMW": # 4/4 time
|
324 |
-
max_expected = min(max_expected,
|
325 |
|
326 |
# Record min and max expected in the template for future reference
|
327 |
template['min_expected'] = min_expected
|
|
|
276 |
visual_pattern += "weak "
|
277 |
|
278 |
# Estimate number of words based on beats (very rough estimate)
|
279 |
+
est_words = max(1, int(num_beats * 0.3)) # Reduced further to encourage extreme brevity
|
280 |
|
281 |
+
# Estimate syllables - use ultra conservative ranges
|
282 |
+
# For 4/4 time signature, we want to enforce extremely short phrases
|
283 |
if stress_pattern == "SWMW": # 4/4 time
|
284 |
+
min_syllables = max(1, int(num_beats * 0.4)) # Reduced from 0.5
|
285 |
+
max_syllables = min(6, int(num_beats * 1.2)) # Reduced from 1.3 to max 6
|
286 |
else:
|
287 |
+
min_syllables = max(1, int(num_beats * 0.4)) # Reduced from 0.5
|
288 |
+
max_syllables = min(6, int(num_beats * 1.1)) # Reduced from 1.2 to max 6
|
289 |
|
290 |
# Store these in the template for future reference
|
291 |
template['min_expected'] = min_syllables
|
|
|
294 |
guide = f"~{est_words} words, ~{min_syllables}-{max_syllables} syllables | Pattern: {visual_pattern}"
|
295 |
|
296 |
# Add additional guidance to the template for natural phrasing
|
297 |
+
template['phrasing_guide'] = "ULTRA SHORT LINES. One thought per line. Use FRAGMENTS not sentences."
|
298 |
|
299 |
return guide
|
300 |
|
|
|
315 |
min_ratio, typical_ratio, max_ratio = self.genre_syllable_ratios['default']
|
316 |
|
317 |
# Calculate flexible min and max syllable expectations based on genre
|
318 |
+
# Use extremely conservative ranges to enforce ultra-short lines
|
319 |
min_expected = max(1, int(expected_count * min_ratio))
|
320 |
+
max_expected = min(6, int(expected_count * max_ratio)) # Hard cap at 6 syllables
|
321 |
|
322 |
+
# For 4/4 time signature, cap the max syllables per line even lower
|
323 |
if template['stress_pattern'] == "SWMW": # 4/4 time
|
324 |
+
max_expected = min(max_expected, 6) # Cap at 6 syllables max for 4/4
|
325 |
|
326 |
# Record min and max expected in the template for future reference
|
327 |
template['min_expected'] = min_expected
|