Skip to content

Commit

Permalink
Merge branch 'master' into audio-stereo-max-volume
Browse files Browse the repository at this point in the history
  • Loading branch information
mondeja committed Jan 18, 2021
2 parents 04d0517 + 7c3dc0a commit e8a994b
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed positioning error generating frames in `CompositeVideoClip` [\#1420](https://github.com/Zulko/moviepy/pull/1420)
- Changed deprecated `tostring` method by `tobytes` in `video.io.gif_writers::write_gif` [\#1429](https://github.com/Zulko/moviepy/pull/1429)
- Fixed calling `audio_normalize` on a clip with no sound causing `ZeroDivisionError` [\#1401](https://github.com/Zulko/moviepy/pull/1401)
- Fixed `freeze` FX was freezing at time minus 1 second as the end [\#1461](https://github.com/Zulko/moviepy/pull/1461)
- `AudioClip.max_volume(stereo=True)` now can return more than 2 channels [\#1464](https://github.com/Zulko/moviepy/pull/1464)


Expand Down
10 changes: 9 additions & 1 deletion moviepy/audio/AudioClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,20 @@ class AudioClip(Clip):
Examples
---------
>>> # Plays the note A (a sine wave of frequency 440HZ)
>>> # Plays the note A in mono (a sine wave of frequency 440 Hz)
>>> import numpy as np
>>> make_frame = lambda t: np.sin(440 * 2 * np.pi * t)
>>> clip = AudioClip(make_frame, duration=5, fps=44100)
>>> clip.preview()
>>> # Plays the note A in stereo (two sine waves of frequencies 440 and 880 Hz)
>>> make_frame = lambda t: np.array([
... np.sin(440 * 2 * np.pi * t),
... np.sin(880 * 2 * np.pi * t)
... ]).T.copy(order="C")
>>> clip = AudioClip(make_frame, duration=3, fps=44100)
>>> clip.preview()
"""

def __init__(self, make_frame=None, duration=None, fps=None):
Expand Down
12 changes: 8 additions & 4 deletions moviepy/video/fx/freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ def freeze(clip, t=0, freeze_duration=None, total_duration=None, padding_end=0):
"""Momentarily freeze the clip at time t.
Set `t='end'` to freeze the clip at the end (actually it will freeze on the
frame at time clip.duration - padding_end seconds).
With ``duration``you can specify the duration of the freeze.
frame at time clip.duration - padding_end seconds - 1 / clip_fps).
With ``duration`` you can specify the duration of the freeze.
With ``total_duration`` you can specify the total duration of
the clip and the freeze (i.e. the duration of the freeze is
automatically calculated). One of them must be provided.
automatically computed). One of them must be provided.
"""

if t == "end":
t = clip.duration - padding_end - 1
t = clip.duration - padding_end - 1 / clip.fps

if freeze_duration is None:
if total_duration is None:
raise ValueError(
"You must provide either 'freeze_duration' or 'total_duration'"
)
freeze_duration = total_duration - clip.duration

before = [clip.subclip(0, t)] if (t != 0) else []
Expand Down
133 changes: 117 additions & 16 deletions tests/test_fx.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,24 +202,127 @@ def test_fadeout():
close_all_clips(locals())


def test_freeze():
clip = BitmapClip([["R"], ["G"], ["B"]], fps=1) # 3 separate frames
@pytest.mark.parametrize(
(
"t",
"freeze_duration",
"total_duration",
"padding_end",
"output_frames",
),
(
# at start, 1 second (default t == 0)
(
None,
1,
None,
None,
["R", "R", "G", "B"],
),
# at start, 1 second (explicit t)
(
0,
1,
None,
None,
["R", "R", "G", "B"],
),
# at end, 1 second
(
"end",
1,
None,
None,
["R", "G", "B", "B"],
),
# at end 1 second, padding end 1 second
(
"end",
1,
None,
1,
["R", "G", "G", "B"],
),
# at 2nd frame, 1 second
(
1, # second 0 is frame 1, second 1 is frame 2...
1,
None,
None,
["R", "G", "G", "B"],
),
# at 2nd frame, 2 seconds
(
1,
2,
None,
None,
["R", "G", "G", "G", "B"],
),
# `freeze_duration`, `total_duration` are None
(1, None, None, None, ValueError),
# `total_duration` 5 at start (2 seconds)
(None, None, 5, None, ["R", "R", "R", "G", "B"]),
# total duration 5 at end
("end", None, 5, None, ["R", "G", "B", "B", "B"]),
# total duration 5 padding end
("end", None, 5, 1, ["R", "G", "G", "G", "B"]),
),
ids=[
"at start, 1 second (default t == 0)",
"at start, 1 second (explicit t)",
"at end, 1 second",
"at end 1 second, padding end 1 second",
"at 2nd frame, 1 second",
"at 2nd frame, 2 seconds",
"`freeze_duration`, `total_duration` are None",
"`total_duration` 5 at start (2 seconds)",
"`total_duration` 5 at end",
"`total_duration` 5 padding end",
],
)
def test_freeze(t, freeze_duration, total_duration, padding_end, output_frames):
input_frames = ["R", "G", "B"]
clip_duration = len(input_frames)

clip1 = freeze(clip, t=1, freeze_duration=1)
target1 = BitmapClip([["R"], ["G"], ["G"], ["B"]], fps=1)
assert clip1 == target1
# create BitmapClip with predefined set of colors, during 1 second each one
clip = BitmapClip([list(color) for color in input_frames], fps=1).with_duration(
clip_duration
)

clip2 = freeze(clip, t="end", freeze_duration=1)
target2 = BitmapClip([["R"], ["G"], ["B"], ["B"]], fps=1)
assert clip2 == target2
# build kwargs passed to `freeze`
possible_kwargs = {
"t": t,
"freeze_duration": freeze_duration,
"total_duration": total_duration,
"padding_end": padding_end,
}
kwargs = {
kw_name: kw_value
for kw_name, kw_value in possible_kwargs.items()
if kw_value is not None
}

clip3 = freeze(clip, t=1, total_duration=4)
target3 = BitmapClip([["R"], ["G"], ["G"], ["B"]], fps=1)
assert clip3 == target3
# freeze clip
if hasattr(output_frames, "__traceback__"):
with pytest.raises(output_frames):
freeze(clip, **kwargs)
return
else:
freezed_clip = freeze(clip, **kwargs)

# assert new duration
expected_freeze_duration = (
freeze_duration
if freeze_duration is not None
else total_duration - clip_duration
)
assert freezed_clip.duration == clip_duration + expected_freeze_duration

clip4 = freeze(clip, t="end", total_duration=4, padding_end=1)
target4 = BitmapClip([["R"], ["G"], ["G"], ["B"]], fps=1)
assert clip4 == target4
# assert colors are the expected
for i, color in enumerate(freezed_clip.iter_frames()):
expected_color = list(BitmapClip.DEFAULT_COLOR_DICT[output_frames[i]])
assert list(color[0][0]) == expected_color


def test_freeze_region():
Expand All @@ -235,8 +338,6 @@ def test_freeze_region():
target2 = BitmapClip([["BBB", "DDD"], ["BBR", "DDD"], ["BBC", "DDD"]], fps=1)
assert clip2 == target2

pass


def test_gamma_corr():
pass
Expand Down

0 comments on commit e8a994b

Please sign in to comment.