jacob-c commited on
Commit
0a49a17
·
1 Parent(s): 6ac84ae
__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
 
app.py CHANGED
@@ -227,19 +227,16 @@ def generate_lyrics(music_analysis, genre, duration):
227
  ONLY WRITE THE ACTUAL LYRICS. NO EXPLANATIONS OR META-TEXT.
228
  """
229
  else:
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}.
@@ -247,27 +244,29 @@ ONLY WRITE THE ACTUAL LYRICS. NO EXPLANATIONS OR META-TEXT.
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)
260
- - Don't include any explanations, thinking tags, or meta-commentary
261
- - Don't use any <think> or [thinking] tags
262
- - Don't include [Verse], [Chorus] or section markers
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
  """
@@ -467,21 +466,71 @@ JUST THE PLAIN LYRICS, EXACTLY {num_phrases} LINES, KEEPING EACH LINE TO {min_sy
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
 
@@ -531,22 +580,26 @@ def analyze_lyrics_rhythm_match(lyrics, lyric_templates, genre="pop"):
531
  check_result = beat_analyzer.check_syllable_stress_match(line, template, genre)
532
 
533
  # Get match symbols
534
- syllable_match = "✓" if check_result["matches_beat_count"] else ("✓*" if check_result["within_range"] else "✗")
 
 
 
 
 
 
535
  stress_match = "✓" if check_result["stress_matches"] else f"{int(check_result['stress_match_percentage']*100)}%"
536
 
537
  # Update stats
538
- if check_result["matches_beat_count"]:
539
  total_matches += 1
540
- if check_result["within_range"]:
 
541
  total_range_matches += 1
 
542
  if check_result["stress_matches"]:
543
  total_stress_matches += 1
544
  total_stress_percentage += check_result["stress_match_percentage"]
545
 
546
- # Track how close we are to ideal count for this genre
547
- if abs(check_result["syllable_count"] - check_result["ideal_syllable_count"]) <= 1:
548
- total_ideal_matches += 1
549
-
550
  # Create visual representation of the stress pattern
551
  stress_visual = ""
552
  for char in template['stress_pattern']:
@@ -563,38 +616,46 @@ def analyze_lyrics_rhythm_match(lyrics, lyric_templates, genre="pop"):
563
  # Add summary statistics
564
  if line_count > 0:
565
  exact_match_rate = (total_matches / line_count) * 100
566
- range_match_rate = (total_range_matches / line_count) * 100
567
  ideal_match_rate = (total_ideal_matches / line_count) * 100
568
  stress_match_rate = (total_stress_matches / line_count) * 100
569
  avg_stress_percentage = (total_stress_percentage / line_count) * 100
570
 
571
  result += f"\n**Summary:**\n"
572
- result += f"- Exact syllable match rate: {exact_match_rate:.1f}%\n"
573
  result += f"- Genre-appropriate syllable range match rate: {range_match_rate:.1f}%\n"
574
- result += f"- Ideal genre syllable count match rate: {ideal_match_rate:.1f}%\n"
575
  result += f"- Perfect stress pattern match rate: {stress_match_rate:.1f}%\n"
576
  result += f"- Average stress pattern accuracy: {avg_stress_percentage:.1f}%\n"
577
  result += f"- Overall rhythmic accuracy: {((range_match_rate + avg_stress_percentage) / 2):.1f}%\n"
578
 
 
 
 
 
 
 
579
  # Add genre-specific notes
580
  result += f"\n**Genre Notes ({genre}):**\n"
581
 
582
  # Add appropriate genre notes based on genre
583
  if genre.lower() == "pop":
584
- result += "- Pop music typically allows 1-3 syllables per beat using melisma and syncopation\n"
585
- result += "- Strong downbeats often align with stressed syllables of important words\n"
586
  elif genre.lower() == "rock":
587
- result += "- Rock music often uses 1-2 syllables per beat with some variation\n"
588
- result += "- Emphasis on strong beats for impact and rhythmic drive\n"
589
- elif genre.lower() in ["hiphop", "rap"]:
590
- result += "- Hip-hop/rap often features 2-5 syllables per beat through rapid delivery\n"
591
- result += "- Complex rhyme patterns and fast delivery create higher syllable density\n"
592
- elif genre.lower() in ["folk", "country"]:
593
- result += "- Folk/country music often stays closer to 1:1 syllable-to-beat ratio\n"
594
- result += "- Narrative focus leads to clearer enunciation of syllables\n"
 
 
 
595
  else:
596
- result += "- This genre typically allows for flexible syllable-to-beat relationships\n"
597
- result += "- Syllable count can vary based on vocal style and song section\n"
598
 
599
  return result
600
 
 
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
233
+ max_syllables = max([t.get('max_expected', 7) for t in lyric_templates]) if lyric_templates[0].get('max_expected') else 7
234
+ min_syllables = min([t.get('min_expected', 2) for t in lyric_templates]) if lyric_templates[0].get('min_expected') else 2
235
  avg_syllables = (min_syllables + max_syllables) // 2
236
  else:
237
+ min_syllables = 2
238
+ max_syllables = 7
239
+ avg_syllables = 4
240
 
241
  # Create a more direct prompt with examples and specific syllable count guidance
242
  prompt = f"""Write song lyrics for a {genre} song in {key} {mode} with tempo {tempo} BPM. The emotion is {emotion} and theme is {theme}.
 
244
  I need EXACTLY {num_phrases} lines of lyrics - one line for each musical phrase. Not one more, not one less.
245
 
246
  CRITICAL INSTRUCTIONS:
247
+ - Each line MUST be VERY SHORT with only {min_syllables}-{max_syllables} syllables (aim for {avg_syllables} or fewer)
248
+ - PRIORITIZE BREVITY - use fewer syllables rather than more
249
+ - Keep each line SIMPLE and DIRECT - avoid complex phrases
250
+ - Break complete thoughts across MULTIPLE LINES rather than fitting them into one line
251
+ - Think of each line as part of a flowing conversation, not a complete sentence
252
+ - Each phrase should fit into one measure of music
253
+ - Use simple, short words whenever possible
254
+ - End each line at a natural speaking pause point
255
 
256
  FORMAT:
257
  - Just write {num_phrases} plain text lines
258
+ - Each line should be simple song lyrics (no annotations)
259
+ - Don't include any explanations or commentary
260
+ - Don't use any tags or markers
261
+ - Don't include section labels like [Verse] or [Chorus]
 
262
 
263
+ EXAMPLE OF WHAT I WANT:
264
+ Empty chair ({min_syllables} syllables)
265
+ Waiting by the door ({avg_syllables} syllables)
266
+ Memories fade ({min_syllables+1} syllables)
267
+ Into silence ({avg_syllables-1} syllables)
268
+ Your ghost remains ({avg_syllables} syllables)
269
+ (... and so on)
270
 
271
  JUST THE PLAIN LYRICS, EXACTLY {num_phrases} LINES, KEEPING EACH LINE TO {min_syllables}-{max_syllables} SYLLABLES.
272
  """
 
466
  i = len(clean_lines)
467
  if i < len(lyric_templates):
468
  template = lyric_templates[i]
469
+ target_syllables = min(max_syllables, (template.get('min_expected', 2) + template.get('max_expected', 7)) // 2)
470
 
471
+ # Create a diverse set of placeholders that match the theme/emotion
472
+ placeholders = {
473
+ # 2-3 syllables
474
+ 2: [
475
+ "Night falls",
476
+ "Time stops",
477
+ "Hearts beat",
478
+ "Rain falls",
479
+ "Stars shine"
480
+ ],
481
+ # 3-4 syllables
482
+ 3: [
483
+ "Empty chair",
484
+ "Shadows dance",
485
+ "Whispers fade",
486
+ "Memories",
487
+ "Silent room"
488
+ ],
489
+ # 4-5 syllables
490
+ 4: [
491
+ "Moonlight shimmers",
492
+ "Echoes of time",
493
+ "Footsteps fading",
494
+ "Memories drift",
495
+ "Silence speaks loud"
496
+ ],
497
+ # 5-6 syllables
498
+ 5: [
499
+ "Walking in the rain",
500
+ "Whispers in the dark",
501
+ "Echoes of your voice",
502
+ "Traces left behind",
503
+ "Time moves ever on"
504
+ ],
505
+ # 6-7 syllables
506
+ 6: [
507
+ "Dancing in the moonlight",
508
+ "Shadows play on the wall",
509
+ "Memories fade to silence",
510
+ "Moments lost in the wind",
511
+ "Whispers of a better time"
512
+ ]
513
+ }
514
+
515
+ # Get the closest matching syllable group
516
+ closest_group = min(placeholders.keys(), key=lambda k: abs(k - target_syllables))
517
+
518
+ # Choose a placeholder that hasn't been used yet
519
+ available_placeholders = [p for p in placeholders[closest_group]
520
+ if p not in clean_lines]
521
+
522
+ if available_placeholders:
523
+ placeholder = available_placeholders[i % len(available_placeholders)]
524
  else:
525
+ # If we've used all placeholders in this group, create a custom one
526
+ if emotion.lower() in ["sad", "nostalgic", "calm"]:
527
+ placeholder = f"Memories of {emotion}"
528
+ elif emotion.lower() in ["happy", "energetic"]:
529
+ placeholder = f"Dancing through {emotion}"
530
+ else:
531
+ placeholder = f"Feeling {emotion} now"
532
  else:
533
+ placeholder = "Silence speaks volumes"
534
 
535
  clean_lines.append(placeholder)
536
 
 
580
  check_result = beat_analyzer.check_syllable_stress_match(line, template, genre)
581
 
582
  # Get match symbols
583
+ if check_result["close_to_ideal"]:
584
+ syllable_match = "✓" # Ideal or very close
585
+ elif check_result["within_range"]:
586
+ syllable_match = "✓*" # Within range but not ideal
587
+ else:
588
+ syllable_match = "✗" # Outside range
589
+
590
  stress_match = "✓" if check_result["stress_matches"] else f"{int(check_result['stress_match_percentage']*100)}%"
591
 
592
  # Update stats
593
+ if check_result["close_to_ideal"]:
594
  total_matches += 1
595
+ total_ideal_matches += 1
596
+ elif check_result["within_range"]:
597
  total_range_matches += 1
598
+
599
  if check_result["stress_matches"]:
600
  total_stress_matches += 1
601
  total_stress_percentage += check_result["stress_match_percentage"]
602
 
 
 
 
 
603
  # Create visual representation of the stress pattern
604
  stress_visual = ""
605
  for char in template['stress_pattern']:
 
616
  # Add summary statistics
617
  if line_count > 0:
618
  exact_match_rate = (total_matches / line_count) * 100
619
+ range_match_rate = ((total_matches + total_range_matches) / line_count) * 100
620
  ideal_match_rate = (total_ideal_matches / line_count) * 100
621
  stress_match_rate = (total_stress_matches / line_count) * 100
622
  avg_stress_percentage = (total_stress_percentage / line_count) * 100
623
 
624
  result += f"\n**Summary:**\n"
625
+ result += f"- Ideal or near-ideal syllable match rate: {exact_match_rate:.1f}%\n"
626
  result += f"- Genre-appropriate syllable range match rate: {range_match_rate:.1f}%\n"
 
627
  result += f"- Perfect stress pattern match rate: {stress_match_rate:.1f}%\n"
628
  result += f"- Average stress pattern accuracy: {avg_stress_percentage:.1f}%\n"
629
  result += f"- Overall rhythmic accuracy: {((range_match_rate + avg_stress_percentage) / 2):.1f}%\n"
630
 
631
+ # Add guidance on ideal distribution for syllables
632
+ result += f"\n**Syllable Distribution Guidance:**\n"
633
+ 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"
634
+ result += f"- Break complete thoughts across multiple lines for a more natural flow\n"
635
+ result += f"- Allow sentences to span 2-3 measures for better musical phrasing\n"
636
+
637
  # Add genre-specific notes
638
  result += f"\n**Genre Notes ({genre}):**\n"
639
 
640
  # Add appropriate genre notes based on genre
641
  if genre.lower() == "pop":
642
+ result += "- Pop lyrics are typically concise with 3-7 syllables per musical phrase\n"
643
+ result += "- Strong beats often align with stressed syllables in important words\n"
644
  elif genre.lower() == "rock":
645
+ result += "- Rock lyrics favor brevity with 3-6 syllables per musical phrase\n"
646
+ result += "- Emphasis on strong beats for rhythmic impact\n"
647
+ elif genre.lower() == "country":
648
+ result += "- Country lyrics tend toward clear storytelling with 3-6 syllables per phrase\n"
649
+ result += "- Natural speech rhythms are important for authentic delivery\n"
650
+ elif genre.lower() == "disco":
651
+ result += "- Disco lyrics work well with 4-7 syllables per musical phrase\n"
652
+ result += "- Rhythmic patterns often emphasize dance-friendly phrasing\n"
653
+ elif genre.lower() == "metal":
654
+ result += "- Metal lyrics balance intensity with 3-7 syllables per musical phrase\n"
655
+ result += "- Strong syllables on strong beats create powerful impact\n"
656
  else:
657
+ result += "- This genre typically works well with concise, focused phrasing\n"
658
+ result += "- Consider breaking complete thoughts across multiple lines\n"
659
 
660
  return result
661
 
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.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,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.7, 1.2, 1.6) # Default for unknown genres - more conservative
53
  }
54
 
55
  # List of genres supported for lyrics generation
@@ -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 * 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
@@ -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'] = "Keep lines short. Split complete thoughts across multiple lines."
298
 
299
  return guide
300
 
@@ -317,11 +317,11 @@ class BeatAnalyzer:
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
@@ -335,6 +335,10 @@ class BeatAnalyzer:
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
@@ -354,7 +358,7 @@ class BeatAnalyzer:
354
  stress_match_percentage = self._calculate_stress_match(words, word_syllables, syllable_to_beat_mapping, stress_pattern)
355
 
356
  # Consider a stress match if the percentage is high enough
357
- stress_matches = stress_match_percentage >= 0.7
358
 
359
  return {
360
  'syllable_count': syllable_count,
@@ -368,7 +372,8 @@ class BeatAnalyzer:
368
  'stress_match_percentage': stress_match_percentage,
369
  'closeness_to_ideal': closeness_to_ideal,
370
  'word_syllables': word_syllables,
371
- 'ideal_syllable_count': ideal_count
 
372
  }
373
 
374
  def _map_syllables_to_beats(self, word_syllables, stress_pattern):
 
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.5, 1.0, 1.5), # Pop - significantly reduced range
36
+ 'rock': (0.5, 0.9, 1.3), # Rock - reduced for brevity
37
+ 'country': (0.6, 0.9, 1.2), # Country - simpler syllable patterns
38
+ 'disco': (0.7, 1.0, 1.3), # Disco - tightened range
39
+ 'metal': (0.6, 1.0, 1.3), # 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.6, 1.0, 1.3) # Default for unknown genres - more conservative
53
  }
54
 
55
  # List of genres supported for lyrics generation
 
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.4)) # Reduced from 0.5 to encourage fewer words
280
 
281
+ # Estimate syllables - use even 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.5)) # Reduced from 0.7
285
+ max_syllables = min(7, int(num_beats * 1.3)) # Reduced from 1.6 to max 7
286
  else:
287
+ min_syllables = max(1, int(num_beats * 0.5)) # Reduced from 0.7
288
+ max_syllables = min(7, int(num_beats * 1.2)) # Reduced from 1.5 to max 7
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'] = "Keep lines SHORT. Break complete thoughts across MULTIPLE LINES."
298
 
299
  return guide
300
 
 
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(7, 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, 7) # Cap at 7 syllables max for 4/4
325
 
326
  # Record min and max expected in the template for future reference
327
  template['min_expected'] = min_expected
 
335
  # Ensure ideal count is also within our constrained range
336
  ideal_count = max(min_expected, min(max_expected, ideal_count))
337
 
338
+ # More lenient approach to determining "ideal"
339
+ # Count as ideal if within 1 syllable of the target instead of exact match
340
+ close_to_ideal = abs(syllable_count - ideal_count) <= 1
341
+
342
  closeness_to_ideal = 1.0 - min(abs(syllable_count - ideal_count) / (max_expected - min_expected + 1), 1.0)
343
 
344
  # Get detailed syllable breakdown for stress analysis
 
358
  stress_match_percentage = self._calculate_stress_match(words, word_syllables, syllable_to_beat_mapping, stress_pattern)
359
 
360
  # Consider a stress match if the percentage is high enough
361
+ stress_matches = stress_match_percentage >= 0.6 # Reduced from 0.7 to be more lenient
362
 
363
  return {
364
  'syllable_count': syllable_count,
 
372
  'stress_match_percentage': stress_match_percentage,
373
  'closeness_to_ideal': closeness_to_ideal,
374
  'word_syllables': word_syllables,
375
+ 'ideal_syllable_count': ideal_count,
376
+ 'close_to_ideal': close_to_ideal # New field
377
  }
378
 
379
  def _map_syllables_to_beats(self, word_syllables, stress_pattern):