ss
Browse files- __pycache__/beat_analysis.cpython-310.pyc +0 -0
- __pycache__/emotionanalysis.cpython-310.pyc +0 -0
- app.py +48 -16
- beat_analysis.py +35 -11
__pycache__/beat_analysis.cpython-310.pyc
CHANGED
|
Binary files a/__pycache__/beat_analysis.cpython-310.pyc and b/__pycache__/beat_analysis.cpython-310.pyc differ
|
|
|
__pycache__/emotionanalysis.cpython-310.pyc
CHANGED
|
Binary files a/__pycache__/emotionanalysis.cpython-310.pyc and b/__pycache__/emotionanalysis.cpython-310.pyc differ
|
|
|
app.py
CHANGED
|
@@ -230,11 +230,30 @@ ONLY WRITE THE ACTUAL LYRICS. NO EXPLANATIONS OR META-TEXT.
|
|
| 230 |
# Create phrase examples
|
| 231 |
num_phrases = len(lyric_templates)
|
| 232 |
|
| 233 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
prompt = f"""Write song lyrics for a {genre} song in {key} {mode} with tempo {tempo} BPM. The emotion is {emotion} and theme is {theme}.
|
| 235 |
|
| 236 |
I need EXACTLY {num_phrases} lines of lyrics - one line for each musical phrase. Not one more, not one less.
|
| 237 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
FORMAT:
|
| 239 |
- Just write {num_phrases} plain text lines
|
| 240 |
- Each line should be simple song lyrics (no annotations, no numbers, no labeling)
|
|
@@ -244,13 +263,13 @@ FORMAT:
|
|
| 244 |
- Don't include line numbers
|
| 245 |
|
| 246 |
EXAMPLE OF WHAT I WANT (for a {num_phrases}-line song):
|
| 247 |
-
Lost in the
|
| 248 |
-
|
| 249 |
-
Time slips
|
| 250 |
-
|
| 251 |
(... and so on for exactly {num_phrases} lines)
|
| 252 |
|
| 253 |
-
JUST THE PLAIN LYRICS, EXACTLY {num_phrases} LINES.
|
| 254 |
"""
|
| 255 |
|
| 256 |
# Generate lyrics using the LLM model
|
|
@@ -427,6 +446,9 @@ JUST THE PLAIN LYRICS, EXACTLY {num_phrases} LINES.
|
|
| 427 |
clean_lines[i] = re.sub(r'</think>', '', clean_lines[i])
|
| 428 |
clean_lines[i] = re.sub(r'\[thinking\]', '', clean_lines[i])
|
| 429 |
clean_lines[i] = re.sub(r'\[/thinking\]', '', clean_lines[i])
|
|
|
|
|
|
|
|
|
|
| 430 |
|
| 431 |
# 9. Filter out any remaining empty lines after tag removal
|
| 432 |
clean_lines = [line for line in clean_lines if line.strip() and not line.isspace()]
|
|
@@ -440,17 +462,27 @@ JUST THE PLAIN LYRICS, EXACTLY {num_phrases} LINES.
|
|
| 440 |
# Keep the first num_required lines
|
| 441 |
clean_lines = clean_lines[:num_required]
|
| 442 |
|
| 443 |
-
# If we don't have enough lines, generate placeholders
|
| 444 |
while len(clean_lines) < num_required:
|
| 445 |
-
|
| 446 |
-
if len(
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
clean_lines.append(placeholder)
|
| 455 |
|
| 456 |
# Assemble final lyrics
|
|
|
|
| 230 |
# Create phrase examples
|
| 231 |
num_phrases = len(lyric_templates)
|
| 232 |
|
| 233 |
+
# Calculate the typical syllable range for this genre
|
| 234 |
+
if num_phrases > 0:
|
| 235 |
+
# Get max syllables per line from templates
|
| 236 |
+
max_syllables = max([t.get('max_expected', 8) for t in lyric_templates]) if lyric_templates[0].get('max_expected') else 8
|
| 237 |
+
min_syllables = min([t.get('min_expected', 3) for t in lyric_templates]) if lyric_templates[0].get('min_expected') else 3
|
| 238 |
+
avg_syllables = (min_syllables + max_syllables) // 2
|
| 239 |
+
else:
|
| 240 |
+
min_syllables = 3
|
| 241 |
+
max_syllables = 8
|
| 242 |
+
avg_syllables = 5
|
| 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 contain between {min_syllables}-{max_syllables} syllables (aim for {avg_syllables})
|
| 251 |
+
- Keep lines SHORT and SIMPLE - fewer syllables is better than too many
|
| 252 |
+
- Break complete thoughts across multiple lines instead of cramming them into one line
|
| 253 |
+
- Each line should flow naturally with the beat
|
| 254 |
+
- Make each line end at a natural pause point
|
| 255 |
+
- Use shorter words when possible
|
| 256 |
+
|
| 257 |
FORMAT:
|
| 258 |
- Just write {num_phrases} plain text lines
|
| 259 |
- Each line should be simple song lyrics (no annotations, no numbers, no labeling)
|
|
|
|
| 263 |
- Don't include line numbers
|
| 264 |
|
| 265 |
EXAMPLE OF WHAT I WANT (for a {num_phrases}-line song):
|
| 266 |
+
Lost in the light ({min_syllables} syllables)
|
| 267 |
+
Waiting for the morning dew ({avg_syllables} syllables)
|
| 268 |
+
Time slips away ({min_syllables+1} syllables)
|
| 269 |
+
In the silence of my room ({avg_syllables} syllables)
|
| 270 |
(... and so on for exactly {num_phrases} lines)
|
| 271 |
|
| 272 |
+
JUST THE PLAIN LYRICS, EXACTLY {num_phrases} LINES, KEEPING EACH LINE TO {min_syllables}-{max_syllables} SYLLABLES.
|
| 273 |
"""
|
| 274 |
|
| 275 |
# Generate lyrics using the LLM model
|
|
|
|
| 446 |
clean_lines[i] = re.sub(r'</think>', '', clean_lines[i])
|
| 447 |
clean_lines[i] = re.sub(r'\[thinking\]', '', clean_lines[i])
|
| 448 |
clean_lines[i] = re.sub(r'\[/thinking\]', '', clean_lines[i])
|
| 449 |
+
|
| 450 |
+
# Remove syllable count annotations
|
| 451 |
+
clean_lines[i] = re.sub(r'\s*\(\d+\s*syllables?\)', '', clean_lines[i])
|
| 452 |
|
| 453 |
# 9. Filter out any remaining empty lines after tag removal
|
| 454 |
clean_lines = [line for line in clean_lines if line.strip() and not line.isspace()]
|
|
|
|
| 462 |
# Keep the first num_required lines
|
| 463 |
clean_lines = clean_lines[:num_required]
|
| 464 |
|
| 465 |
+
# If we don't have enough lines, generate placeholders that fit the syllable count
|
| 466 |
while len(clean_lines) < num_required:
|
| 467 |
+
i = len(clean_lines)
|
| 468 |
+
if i < len(lyric_templates):
|
| 469 |
+
template = lyric_templates[i]
|
| 470 |
+
target_syllables = min(max_syllables, (template.get('min_expected', 3) + template.get('max_expected', 8)) // 2)
|
| 471 |
+
|
| 472 |
+
if genre.lower() == 'pop':
|
| 473 |
+
if target_syllables <= 4:
|
| 474 |
+
placeholder = "Lost in the night" # 4 syllables
|
| 475 |
+
else:
|
| 476 |
+
placeholder = "Dancing in the moonlight" # 6 syllables
|
| 477 |
+
elif genre.lower() == 'rock':
|
| 478 |
+
placeholder = "Rocking to the beat" # 5 syllables
|
| 479 |
+
elif genre.lower() == 'country':
|
| 480 |
+
placeholder = "Down the old dirt road" # 5 syllables
|
| 481 |
+
else:
|
| 482 |
+
placeholder = f"Echoes of {emotion}" # ~4-5 syllables
|
| 483 |
+
else:
|
| 484 |
+
placeholder = "Whispers in the wind" # 5 syllables
|
| 485 |
+
|
| 486 |
clean_lines.append(placeholder)
|
| 487 |
|
| 488 |
# Assemble final lyrics
|
beat_analysis.py
CHANGED
|
@@ -32,11 +32,11 @@ class BeatAnalyzer:
|
|
| 32 |
# Genre-specific syllable-to-beat ratio guidelines
|
| 33 |
self.genre_syllable_ratios = {
|
| 34 |
# Supported genres with strong syllable-to-beat patterns
|
| 35 |
-
'pop': (0.
|
| 36 |
-
'rock': (0.
|
| 37 |
-
'country': (0.
|
| 38 |
-
'disco': (
|
| 39 |
-
'metal': (0.
|
| 40 |
|
| 41 |
# Other genres (analysis only, no lyrics generation)
|
| 42 |
'hiphop': (1.8, 2.5, 3.5), # Hip hop often has many syllables per beat
|
|
@@ -49,7 +49,7 @@ class BeatAnalyzer:
|
|
| 49 |
'electronic': (0.7, 1.0, 1.5), # Electronic music varies widely
|
| 50 |
'classical': (0.7, 1.0, 1.4), # Classical can vary by subgenre
|
| 51 |
'blues': (0.6, 0.8, 1.2), # Blues often extends syllables
|
| 52 |
-
'default': (0.
|
| 53 |
}
|
| 54 |
|
| 55 |
# List of genres supported for lyrics generation
|
|
@@ -278,12 +278,24 @@ class BeatAnalyzer:
|
|
| 278 |
# Estimate number of words based on beats (very rough estimate)
|
| 279 |
est_words = max(1, int(num_beats * words_per_beat))
|
| 280 |
|
| 281 |
-
# Estimate syllables - more
|
| 282 |
-
#
|
| 283 |
-
|
| 284 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
|
| 286 |
guide = f"~{est_words} words, ~{min_syllables}-{max_syllables} syllables | Pattern: {visual_pattern}"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
return guide
|
| 288 |
|
| 289 |
def check_syllable_stress_match(self, text, template, genre="pop"):
|
|
@@ -303,14 +315,26 @@ class BeatAnalyzer:
|
|
| 303 |
min_ratio, typical_ratio, max_ratio = self.genre_syllable_ratios['default']
|
| 304 |
|
| 305 |
# Calculate flexible min and max syllable expectations based on genre
|
|
|
|
| 306 |
min_expected = max(1, int(expected_count * min_ratio))
|
| 307 |
-
max_expected = int(expected_count * max_ratio)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
|
| 309 |
# Check if syllable count falls within genre-appropriate range
|
| 310 |
within_range = min_expected <= syllable_count <= max_expected
|
| 311 |
|
| 312 |
# Consider typical ratio - how close are we to the ideal for this genre?
|
| 313 |
ideal_count = int(expected_count * typical_ratio)
|
|
|
|
|
|
|
|
|
|
| 314 |
closeness_to_ideal = 1.0 - min(abs(syllable_count - ideal_count) / (max_expected - min_expected + 1), 1.0)
|
| 315 |
|
| 316 |
# Get detailed syllable breakdown for stress analysis
|
|
|
|
| 32 |
# Genre-specific syllable-to-beat ratio guidelines
|
| 33 |
self.genre_syllable_ratios = {
|
| 34 |
# Supported genres with strong syllable-to-beat patterns
|
| 35 |
+
'pop': (0.7, 1.2, 1.6), # Pop - more conservative range
|
| 36 |
+
'rock': (0.7, 1.0, 1.5), # Rock - slightly reduced upper range
|
| 37 |
+
'country': (0.7, 1.0, 1.3), # Country - clear and simple syllable patterns
|
| 38 |
+
'disco': (0.8, 1.2, 1.5), # Disco - tighter range for better alignment
|
| 39 |
+
'metal': (0.7, 1.2, 1.5), # Metal - reduced upper limit
|
| 40 |
|
| 41 |
# Other genres (analysis only, no lyrics generation)
|
| 42 |
'hiphop': (1.8, 2.5, 3.5), # Hip hop often has many syllables per beat
|
|
|
|
| 49 |
'electronic': (0.7, 1.0, 1.5), # Electronic music varies widely
|
| 50 |
'classical': (0.7, 1.0, 1.4), # Classical can vary by subgenre
|
| 51 |
'blues': (0.6, 0.8, 1.2), # Blues often extends syllables
|
| 52 |
+
'default': (0.7, 1.2, 1.6) # Default for unknown genres - more conservative
|
| 53 |
}
|
| 54 |
|
| 55 |
# List of genres supported for lyrics generation
|
|
|
|
| 278 |
# Estimate number of words based on beats (very rough estimate)
|
| 279 |
est_words = max(1, int(num_beats * words_per_beat))
|
| 280 |
|
| 281 |
+
# Estimate syllables - use more conservative ranges
|
| 282 |
+
# For 4/4 time signature, we want to encourage shorter phrases
|
| 283 |
+
if stress_pattern == "SWMW": # 4/4 time
|
| 284 |
+
min_syllables = max(1, int(num_beats * 0.7))
|
| 285 |
+
max_syllables = min(8, int(num_beats * 1.6))
|
| 286 |
+
else:
|
| 287 |
+
min_syllables = max(1, int(num_beats * 0.7))
|
| 288 |
+
max_syllables = int(num_beats * 1.5)
|
| 289 |
+
|
| 290 |
+
# Store these in the template for future reference
|
| 291 |
+
template['min_expected'] = min_syllables
|
| 292 |
+
template['max_expected'] = max_syllables
|
| 293 |
|
| 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'] = "Keep lines short. Split complete thoughts across multiple lines."
|
| 298 |
+
|
| 299 |
return guide
|
| 300 |
|
| 301 |
def check_syllable_stress_match(self, text, template, genre="pop"):
|
|
|
|
| 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 more conservative ranges to avoid too many syllables
|
| 319 |
min_expected = max(1, int(expected_count * min_ratio))
|
| 320 |
+
max_expected = min(8, int(expected_count * max_ratio))
|
| 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, 8) # Cap at 8 syllables max for 4/4
|
| 325 |
+
|
| 326 |
+
# Record min and max expected in the template for future reference
|
| 327 |
+
template['min_expected'] = min_expected
|
| 328 |
+
template['max_expected'] = max_expected
|
| 329 |
|
| 330 |
# Check if syllable count falls within genre-appropriate range
|
| 331 |
within_range = min_expected <= syllable_count <= max_expected
|
| 332 |
|
| 333 |
# Consider typical ratio - how close are we to the ideal for this genre?
|
| 334 |
ideal_count = int(expected_count * typical_ratio)
|
| 335 |
+
# Ensure ideal count is also within our constrained range
|
| 336 |
+
ideal_count = max(min_expected, min(max_expected, ideal_count))
|
| 337 |
+
|
| 338 |
closeness_to_ideal = 1.0 - min(abs(syllable_count - ideal_count) / (max_expected - min_expected + 1), 1.0)
|
| 339 |
|
| 340 |
# Get detailed syllable breakdown for stress analysis
|