initial commit and version 1.0

This commit is contained in:
2025-04-21 15:14:03 +02:00
commit ae6b2bbf44
82 changed files with 10782 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
from dataclasses import dataclass
import numpy as np
from moviepy.audio.AudioClip import CompositeAudioClip
from moviepy.audio.fx.MultiplyVolume import MultiplyVolume
from moviepy.Clip import Clip
from moviepy.decorators import audio_video_effect
from moviepy.Effect import Effect
@dataclass
class AudioDelay(Effect):
"""Repeats audio certain number of times at constant intervals multiplying
their volume levels using a linear space in the range 1 to ``decay`` argument
value.
Parameters
----------
offset : float, optional
Gap between repetitions start times, in seconds.
n_repeats : int, optional
Number of repetitions (without including the clip itself).
decay : float, optional
Multiplication factor for the volume level of the last repetition. Each
repetition will have a value in the linear function between 1 and this value,
increasing or decreasing constantly. Keep in mind that the last repetition
will be muted if this is 0, and if is greater than 1, the volume will increase
for each repetition.
Examples
--------
.. code:: python
from moviepy import *
videoclip = AudioFileClip('myaudio.wav').with_effects([
afx.AudioDelay(offset=.2, n_repeats=10, decayment=.2)
])
# stereo A note
frame_function = lambda t: np.array(
[np.sin(440 * 2 * np.pi * t), np.sin(880 * 2 * np.pi * t)]
).T
clip = AudioClip(frame_function=frame_function, duration=0.1, fps=44100)
clip = clip.with_effects([afx.AudioDelay(offset=.2, n_repeats=11, decay=0)])
"""
offset: float = 0.2
n_repeats: int = 8
decay: float = 1
@audio_video_effect
def apply(self, clip: Clip) -> Clip:
"""Apply the effect to the clip."""
decayments = np.linspace(1, max(0, self.decay), self.n_repeats + 1)
return CompositeAudioClip(
[
clip.copy(),
*[
clip.with_start((rep + 1) * self.offset).with_effects(
[MultiplyVolume(decayments[rep + 1])]
)
for rep in range(self.n_repeats)
],
]
)

View File

@@ -0,0 +1,60 @@
from dataclasses import dataclass
import numpy as np
from moviepy.Clip import Clip
from moviepy.decorators import audio_video_effect
from moviepy.Effect import Effect
from moviepy.tools import convert_to_seconds
@dataclass
class AudioFadeIn(Effect):
"""Return an audio (or video) clip that is first mute, then the
sound arrives progressively over ``duration`` seconds.
Parameters
----------
duration : float
How long does it take for the sound to return to its normal level.
Examples
--------
.. code:: python
clip = VideoFileClip("media/chaplin.mp4")
clip.with_effects([afx.AudioFadeIn("00:00:06")])
"""
duration: float
def __post_init__(self):
self.duration = convert_to_seconds(self.duration)
def _mono_factor_getter(self):
return lambda t, duration: np.minimum(t / duration, 1)
def _stereo_factor_getter(self, nchannels):
def getter(t, duration):
factor = np.minimum(t / duration, 1)
return np.array([factor for _ in range(nchannels)]).T
return getter
@audio_video_effect
def apply(self, clip: Clip) -> Clip:
"""Apply the effect to the clip."""
if clip.duration is None:
raise ValueError("Attribute 'duration' not set")
get_factor = (
self._mono_factor_getter()
if clip.nchannels == 1
else self._stereo_factor_getter(clip.nchannels)
)
return clip.transform(
lambda get_frame, t: get_factor(t, self.duration) * get_frame(t),
)

View File

@@ -0,0 +1,62 @@
from dataclasses import dataclass
import numpy as np
from moviepy.Clip import Clip
from moviepy.decorators import audio_video_effect
from moviepy.Effect import Effect
from moviepy.tools import convert_to_seconds
@dataclass
class AudioFadeOut(Effect):
"""Return a sound clip where the sound fades out progressively
over ``duration`` seconds at the end of the clip.
Parameters
----------
duration : float
How long does it take for the sound to reach the zero level at the end
of the clip.
Examples
--------
.. code:: python
clip = VideoFileClip("media/chaplin.mp4")
clip.with_effects([afx.AudioFadeOut("00:00:06")])
"""
duration: float
def __post_init__(self):
self.duration = convert_to_seconds(self.duration)
def _mono_factor_getter(self, clip_duration):
return lambda t, duration: np.minimum(1.0 * (clip_duration - t) / duration, 1)
def _stereo_factor_getter(self, clip_duration, nchannels):
def getter(t, duration):
factor = np.minimum(1.0 * (clip_duration - t) / duration, 1)
return np.array([factor for _ in range(nchannels)]).T
return getter
@audio_video_effect
def apply(self, clip: Clip) -> Clip:
"""Apply the effect to the clip."""
if clip.duration is None:
raise ValueError("Attribute 'duration' not set")
get_factor = (
self._mono_factor_getter(clip.duration)
if clip.nchannels == 1
else self._stereo_factor_getter(clip.duration, clip.nchannels)
)
return clip.transform(
lambda get_frame, t: get_factor(t, self.duration) * get_frame(t),
keep_duration=True,
)

View File

@@ -0,0 +1,41 @@
from dataclasses import dataclass
from moviepy.audio.AudioClip import concatenate_audioclips
from moviepy.Clip import Clip
from moviepy.decorators import audio_video_effect
from moviepy.Effect import Effect
@dataclass
class AudioLoop(Effect):
"""Loops over an audio clip.
Returns an audio clip that plays the given clip either
`n_loops` times, or during `duration` seconds.
Examples
--------
.. code:: python
from moviepy import *
videoclip = VideoFileClip('myvideo.mp4')
music = AudioFileClip('music.ogg')
audio = music.with_effects([afx.AudioLoop(duration=videoclip.duration)])
videoclip.with_audio(audio)
"""
n_loops: int = None
duration: float = None
@audio_video_effect
def apply(self, clip: Clip) -> Clip:
"""Apply the effect to the clip."""
if self.duration is not None:
self.n_loops = int(self.duration / clip.duration) + 1
return concatenate_audioclips(self.n_loops * [clip]).with_duration(
self.duration
)
return concatenate_audioclips(self.n_loops * [clip])

View File

@@ -0,0 +1,31 @@
from dataclasses import dataclass
from moviepy.audio.fx.MultiplyVolume import MultiplyVolume
from moviepy.Clip import Clip
from moviepy.decorators import audio_video_effect
from moviepy.Effect import Effect
@dataclass
class AudioNormalize(Effect):
"""Return a clip whose volume is normalized to 0db.
Return an audio (or video) clip whose audio volume is normalized
so that the maximum volume is at 0db, the maximum achievable volume.
Examples
--------
>>> from moviepy import *
>>> videoclip = VideoFileClip('myvideo.mp4').with_effects([afx.AudioNormalize()])
"""
@audio_video_effect
def apply(self, clip: Clip) -> Clip:
"""Apply the effect to the clip."""
max_volume = clip.max_volume()
if max_volume == 0:
return clip
else:
return clip.with_effects([MultiplyVolume(1 / max_volume)])

View File

@@ -0,0 +1,44 @@
from dataclasses import dataclass
from moviepy.Clip import Clip
from moviepy.decorators import audio_video_effect
from moviepy.Effect import Effect
@dataclass
class MultiplyStereoVolume(Effect):
"""For a stereo audioclip, this function enables to change the volume
of the left and right channel separately (with the factors `left`
and `right`). Makes a stereo audio clip in which the volume of left
and right is controllable.
Examples
--------
.. code:: python
from moviepy import AudioFileClip
music = AudioFileClip('music.ogg')
# mutes left channel
audio_r = music.with_effects([afx.MultiplyStereoVolume(left=0, right=1)])
# halves audio volume
audio_h = music.with_effects([afx.MultiplyStereoVolume(left=0.5, right=0.5)])
"""
left: float = 1
right: float = 1
@audio_video_effect
def apply(self, clip: Clip) -> Clip:
"""Apply the effect to the clip."""
def stereo_volume(get_frame, t):
frame = get_frame(t)
if len(frame) == 1: # mono
frame *= self.left if self.left is not None else self.right
else: # stereo, stereo surround...
for i in range(len(frame[0])): # odd channels are left
frame[:, i] *= self.left if i % 2 == 0 else self.right
return frame
return clip.transform(stereo_volume)

View File

@@ -0,0 +1,90 @@
from dataclasses import dataclass
import numpy as np
from moviepy.Clip import Clip
from moviepy.decorators import audio_video_effect
from moviepy.Effect import Effect
from moviepy.tools import convert_to_seconds
@dataclass
class MultiplyVolume(Effect):
"""Returns a clip with audio volume multiplied by the
value `factor`. Can be applied to both audio and video clips.
Parameters
----------
factor : float
Volume multiplication factor.
start_time : float, optional
Time from the beginning of the clip until the volume transformation
begins to take effect, in seconds. By default at the beginning.
end_time : float, optional
Time from the beginning of the clip until the volume transformation
ends to take effect, in seconds. By default at the end.
Examples
--------
.. code:: python
from moviepy import AudioFileClip
music = AudioFileClip("music.ogg")
# doubles audio volume
doubled_audio_clip = music.with_effects([afx.MultiplyVolume(2)])
# halves audio volume
half_audio_clip = music.with_effects([afx.MultiplyVolume(0.5)])
# silences clip during one second at third
effect = afx.MultiplyVolume(0, start_time=2, end_time=3)
silenced_clip = clip.with_effects([effect])
"""
factor: float
start_time: float = None
end_time: float = None
def __post_init__(self):
if self.start_time is not None:
self.start_time = convert_to_seconds(self.start_time)
if self.end_time is not None:
self.end_time = convert_to_seconds(self.end_time)
def _multiply_volume_in_range(self, factor, start_time, end_time, nchannels):
def factors_filter(factor, t):
return np.array([factor if start_time <= t_ <= end_time else 1 for t_ in t])
def multiply_stereo_volume(get_frame, t):
return np.multiply(
get_frame(t),
np.array([factors_filter(factor, t) for _ in range(nchannels)]).T,
)
def multiply_mono_volume(get_frame, t):
return np.multiply(get_frame(t), factors_filter(factor, t))
return multiply_mono_volume if nchannels == 1 else multiply_stereo_volume
@audio_video_effect
def apply(self, clip: Clip) -> Clip:
"""Apply the effect to the clip."""
if self.start_time is None and self.end_time is None:
return clip.transform(
lambda get_frame, t: self.factor * get_frame(t),
keep_duration=True,
)
return clip.transform(
self._multiply_volume_in_range(
self.factor,
clip.start if self.start_time is None else self.start_time,
clip.end if self.end_time is None else self.end_time,
clip.nchannels,
),
keep_duration=True,
)

View File

@@ -0,0 +1,22 @@
"""All the audio effects that can be applied to AudioClip and VideoClip."""
# import every video fx function
from moviepy.audio.fx.AudioDelay import AudioDelay
from moviepy.audio.fx.AudioFadeIn import AudioFadeIn
from moviepy.audio.fx.AudioFadeOut import AudioFadeOut
from moviepy.audio.fx.AudioLoop import AudioLoop
from moviepy.audio.fx.AudioNormalize import AudioNormalize
from moviepy.audio.fx.MultiplyStereoVolume import MultiplyStereoVolume
from moviepy.audio.fx.MultiplyVolume import MultiplyVolume
__all__ = (
"AudioDelay",
"AudioFadeIn",
"AudioFadeOut",
"AudioLoop",
"AudioNormalize",
"MultiplyStereoVolume",
"MultiplyVolume",
)