chore: atualização geral
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
"""Tests for fcpxml/emphasis.py — the emphasis index (pure, no audio needed)."""
|
||||
|
||||
from fcpxml.emphasis import (
|
||||
EmphasisWeights,
|
||||
annotate_emphasis,
|
||||
compute_emphasis,
|
||||
pause_weight,
|
||||
)
|
||||
|
||||
|
||||
def test_zero_input_gives_zero_score():
|
||||
assert compute_emphasis(0.0, 0.0, 0.0, 0.0, 0.0) == 0.0
|
||||
|
||||
|
||||
def test_max_input_gives_max_score():
|
||||
# pause sits inside the "dramatic beat" window, not a scene-change gap
|
||||
score = compute_emphasis(
|
||||
energy=1.0, pitch_delta=1.0, rate_delta=1.0, pause_before=1.5, word_duration=10.0
|
||||
)
|
||||
assert score == 1.0
|
||||
|
||||
|
||||
def test_negative_pitch_delta_uses_magnitude():
|
||||
a = compute_emphasis(0.0, pitch_delta=0.5, rate_delta=0.0, pause_before=0.0, word_duration=0.0)
|
||||
b = compute_emphasis(0.0, pitch_delta=-0.5, rate_delta=0.0, pause_before=0.0, word_duration=0.0)
|
||||
assert a == b > 0.0
|
||||
|
||||
|
||||
def test_duration_saturates_beyond_cap():
|
||||
at_cap = compute_emphasis(0.0, 0.0, 0.0, 0.0, word_duration=1.0, max_duration=1.0)
|
||||
beyond = compute_emphasis(0.0, 0.0, 0.0, 0.0, word_duration=30.0, max_duration=1.0)
|
||||
assert at_cap == beyond
|
||||
|
||||
|
||||
class TestPauseWeight:
|
||||
"""A long gap is a scene change, not emphasis — it must not outrank a
|
||||
word the speaker actually hit hard. Grounded in real footage where 6-9s
|
||||
gaps were topping the emphasis ranking."""
|
||||
|
||||
def test_dramatic_beat_counts_fully(self):
|
||||
assert pause_weight(1.5, max_pause=1.5) == 1.0
|
||||
|
||||
def test_short_beat_counts_proportionally(self):
|
||||
assert pause_weight(0.75, max_pause=1.5) == 0.5
|
||||
|
||||
def test_long_gap_is_ignored(self):
|
||||
assert pause_weight(8.7, max_pause=1.5, ignore_above=3.0) == 0.0
|
||||
|
||||
def test_no_pause_is_zero(self):
|
||||
assert pause_weight(0.0) == 0.0
|
||||
|
||||
def test_cutoff_can_be_disabled(self):
|
||||
assert pause_weight(30.0, max_pause=1.5, ignore_above=0.0) == 1.0
|
||||
|
||||
def test_scene_change_scores_below_a_loud_word(self):
|
||||
gap = compute_emphasis(0.25, 0.04, 0.0, pause_before=6.2, word_duration=0.5)
|
||||
loud = compute_emphasis(1.00, 0.28, 0.0, pause_before=1.9, word_duration=0.2)
|
||||
assert loud > gap
|
||||
|
||||
|
||||
def test_higher_energy_weight_increases_energy_contribution():
|
||||
low_weight = EmphasisWeights(energy=0.1, pitch_variation=0.0, rate_variation=0.0, pause_before=0.0, duration=0.0)
|
||||
high_weight = EmphasisWeights(energy=1.0, pitch_variation=0.0, rate_variation=0.0, pause_before=0.0, duration=0.0)
|
||||
# energy is the only nonzero factor for both weight sets, so normalized
|
||||
# score should be identical regardless of the absolute weight value.
|
||||
score_low = compute_emphasis(0.6, 0.0, 0.0, 0.0, 0.0, weights=low_weight)
|
||||
score_high = compute_emphasis(0.6, 0.0, 0.0, 0.0, 0.0, weights=high_weight)
|
||||
assert score_low == score_high
|
||||
|
||||
|
||||
def test_all_zero_weights_returns_zero_not_error():
|
||||
zero_weights = EmphasisWeights(0.0, 0.0, 0.0, 0.0, 0.0)
|
||||
assert compute_emphasis(1.0, 1.0, 1.0, 1.0, 1.0, weights=zero_weights) == 0.0
|
||||
|
||||
|
||||
def test_weights_round_trip_dict():
|
||||
w = EmphasisWeights(energy=0.4, pitch_variation=0.3, rate_variation=0.1, pause_before=0.1, duration=0.1)
|
||||
restored = EmphasisWeights.from_dict(w.as_dict())
|
||||
assert restored == w
|
||||
|
||||
|
||||
def test_from_dict_fills_missing_with_defaults():
|
||||
restored = EmphasisWeights.from_dict({"energy": 0.9})
|
||||
defaults = EmphasisWeights()
|
||||
assert restored.energy == 0.9
|
||||
assert restored.pitch_variation == defaults.pitch_variation
|
||||
|
||||
|
||||
def test_annotate_emphasis_adds_score_per_word():
|
||||
words = [
|
||||
{"word": "hi", "start": 0.0, "end": 0.3, "energy": 0.2, "pitch_delta": 0.1, "rate_delta": 0.1, "pause_before": 0.0},
|
||||
{"word": "WOW", "start": 1.0, "end": 1.5, "energy": 0.9, "pitch_delta": 0.8, "rate_delta": 0.7, "pause_before": 1.0},
|
||||
]
|
||||
annotated = annotate_emphasis(words)
|
||||
assert len(annotated) == 2
|
||||
assert all("emphasis" in w for w in annotated)
|
||||
assert annotated[1]["emphasis"] > annotated[0]["emphasis"]
|
||||
|
||||
|
||||
def test_annotate_emphasis_does_not_mutate_input():
|
||||
words = [{"word": "hi", "start": 0.0, "end": 0.3, "energy": 0.5}]
|
||||
annotate_emphasis(words)
|
||||
assert "emphasis" not in words[0]
|
||||
|
||||
|
||||
def test_annotate_emphasis_derives_duration_from_start_end():
|
||||
"""A word with no explicit "duration" key gets it from end - start."""
|
||||
base = {"energy": 0.0, "pitch_delta": 0.0, "rate_delta": 0.0, "pause_before": 0.0}
|
||||
derived = annotate_emphasis([{"word": "hi", "start": 1.0, "end": 1.5, **base}], max_duration=0.5)
|
||||
explicit = annotate_emphasis([{"word": "hi", "start": 0.0, "end": 0.0, "duration": 0.5, **base}], max_duration=0.5)
|
||||
# duration is the only nonzero factor in both, so scores must match
|
||||
assert derived[0]["emphasis"] == explicit[0]["emphasis"] > 0.0
|
||||
Reference in New Issue
Block a user