"""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