Skip to content

Commit

Permalink
Add 'multiply_stereo_volume' FX; rename 'volumex' to 'multiply_volume' (
Browse files Browse the repository at this point in the history
  • Loading branch information
mondeja authored Jan 15, 2021
1 parent b0558c2 commit 20309f1
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 53 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added <!-- for new features -->
- `VideoFileClip.coreader()` that creates a new clip created from the same source file as the original (to complement the existing `AudioFileClip.coreader()`) [\#1332](https://github.com/Zulko/moviepy/pull/1332)
- `audio.fx.multiply_stereo_volume` to control volume by audio channels [\#1424](https://github.com/Zulko/moviepy/pull/1424)

### Changed <!-- for changes in existing functionality -->
- Lots of method and parameter names have been changed. This will be explained better in the documentation soon. See https://github.com/Zulko/moviepy/pull/1170 for more information. [\#1170](https://github.com/Zulko/moviepy/pull/1170)
- Changed recommended import from `import moviepy.editor` to `import moviepy`. This change is fully backwards compatible [\#1340](https://github.com/Zulko/moviepy/pull/1340)
- Renamed `audio.fx.volumex` to `audio.fx.multiply_volume` [\#1424](https://github.com/Zulko/moviepy/pull/1424)

### Deprecated <!-- for soon-to-be removed features -->
- `moviepy.video.fx.all` and `moviepy.audio.fx.all`. Use the fx method directly from the clip instance or import the fx function from `moviepy.video.fx` and `moviepy.audio.fx`. [\#1105](https://github.com/Zulko/moviepy/pull/1105)
Expand Down
2 changes: 1 addition & 1 deletion docs/getting_started/compositing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ If you want to make a custom audiotrack from several audio sources: audioc clips
from moviepy import *
# ... make some audio clips aclip1, aclip2, aclip3
concat = concatenate_audioclips([aclip1, aclip2, aclip3])
compo = CompositeAudioClip([aclip1.volumex(1.2),
compo = CompositeAudioClip([aclip1.multiply_volume(1.2),
aclip2.with_start(5), # start at t=5s
aclip3.with_start(9)])

2 changes: 1 addition & 1 deletion docs/getting_started/quick_presentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ In a typical MoviePy script, you load video or audio files, modify them, put the
clip = VideoFileClip("myHolidays.mp4").subclip(50,60)

# Reduce the audio volume (volume x 0.8)
clip = clip.volumex(0.8)
clip = clip.multiply_volume(0.8)

# Generate a text clip. You can customize the font, color, etc.
txt_clip = TextClip("My Holidays 2013",fontsize=70,color='white')
Expand Down
13 changes: 7 additions & 6 deletions docs/ref/audiofx.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ audio.fx


The module ``moviepy.audio.fx`` regroups functions meant to be used with ``audio.fx()``.
Note that some of these functions such as ``volumex`` (which multiplies the volume) can
Note that some of these functions such as ``multiply_volume`` (which multiplies the volume) can
be applied directly to a video clip, at which case they will affect the audio clip attached to this
video clip. Read the docs of the different functions to know when this is the case.

Because this module will be larger in the future, it allows two kinds of import.
You can either import a single function like this: ::
from moviepy.audio.fx.volumex import volumex
newaudio = audioclip.fx( vfx.volumex, 0.5)
from moviepy.audio.fx.multiply_volume import multiply_volume
newaudio = audioclip.fx(multiply_volume, 0.5)

Or import everything: ::
import moviepy.audio.fx.all as afx
newaudio = (audioclip.afx( vfx.normalize)
.afx( vfx.volumex, 0.5)
.afx( vfx.multiply_volume, 0.5)
.afx( vfx.audio_fadein, 1.0)
.afx( vfx.audio_fadeout, 1.0))

Expand All @@ -30,7 +30,7 @@ When you type ::
from moviepy import *

the module ``audio.fx`` is loaded as ``afx`` and you can use ``afx.volumex``, etc.
the module ``audio.fx`` is loaded as ``afx`` and you can use ``afx.multiply_volume``, etc.


.. currentmodule:: moviepy.audio.fx.all
Expand All @@ -43,4 +43,5 @@ the module ``audio.fx`` is loaded as ``afx`` and you can use ``afx.volumex``, et
audio_fadeout
audio_loop
audio_normalize
volumex
multiply_stereo_volume
multiply_volume
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
moviepy.audio.fx.all.multiply_stereo_volume
===========================================

.. currentmodule:: moviepy.audio.fx.all

.. autofunction:: multiply_stereo_volume
6 changes: 6 additions & 0 deletions docs/ref/audiofx/moviepy.audio.fx.all.multiply_volume.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
moviepy.audio.fx.all.multiply_volume
====================================

.. currentmodule:: moviepy.audio.fx.all

.. autofunction:: multiply_volume
5 changes: 4 additions & 1 deletion examples/example_with_sound.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@
# ASSEMBLE AND WRITE THE MOVIE TO A FILE

cc = CompositeVideoClip(
[clip_right.set_pos("right").volumex(0.4), clip_left.set_pos("left").volumex(0.4)],
[
clip_right.set_pos("right").multiply_volume(0.4),
clip_left.set_pos("left").multiply_volume(0.4),
],
size=(W, H),
)
# cc.preview()
Expand Down
6 changes: 3 additions & 3 deletions moviepy/Clip.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ def fx(self, func, *args, **kwargs):
The motivation of fx is to keep the name of the effect near its
parameters when the effects are chained:
>>> from moviepy.video.fx import volumex, resize, mirrorx
>>> clip.fx(volumex, 0.5).fx(resize, 0.3).fx(mirrorx)
>>> from moviepy.video.fx import multiply_volume, resize, mirrorx
>>> clip.fx(multiply_volume, 0.5).fx(resize, 0.3).fx(mirrorx)
>>> # Is equivalent, but clearer than
>>> mirrorx(resize(volumex(clip, 0.5), 0.3))
>>> mirrorx(resize(multiply_volume(clip, 0.5), 0.3))
"""

return func(self, *args, **kwargs)
Expand Down
4 changes: 2 additions & 2 deletions moviepy/audio/fx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from .audio_fadein import audio_fadein
from .audio_fadeout import audio_fadeout
from .audio_left_right import audio_left_right
from .audio_loop import audio_loop
from .audio_normalize import audio_normalize
from .volumex import volumex
from .multiply_stereo_volume import multiply_stereo_volume
from .multiply_volume import multiply_volume
15 changes: 0 additions & 15 deletions moviepy/audio/fx/audio_left_right.py

This file was deleted.

4 changes: 2 additions & 2 deletions moviepy/audio/fx/audio_normalize.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from moviepy.decorators import audio_video_fx

from .volumex import volumex
from .multiply_volume import multiply_volume


@audio_video_fx
Expand All @@ -24,4 +24,4 @@ def audio_normalize(clip):
# Avoids a divide by zero error.
return clip.copy()
else:
return volumex(clip, 1 / max_volume)
return multiply_volume(clip, 1 / max_volume)
32 changes: 32 additions & 0 deletions moviepy/audio/fx/multiply_stereo_volume.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import numpy as np

from moviepy.decorators import audio_video_fx


@audio_video_fx
def multiply_stereo_volume(clip, left=1, right=1):
"""
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
========
>>> from moviepy import AudioFileClip
>>> music = AudioFileClip('music.ogg')
>>> audio_r = music.multiply_stereo_volume(left=0, right=1) # mute left channel/s
>>> audio_h = music.multiply_stereo_volume(left=0.5, right=0.5) # half audio
"""

def stereo_volume(get_frame, t):
frame = get_frame(t)
if len(frame) == 1: # mono
frame *= left if left is not None else right
else: # stereo, stereo surround...
for i in range(len(frame[0])): # odd channels are left
frame[:, i] *= left if i % 2 == 0 else right
return frame

return clip.transform(stereo_volume, keep_duration=True)
22 changes: 22 additions & 0 deletions moviepy/audio/fx/multiply_volume.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from moviepy.decorators import audio_video_fx


@audio_video_fx
def multiply_volume(clip, factor):
"""Returns a clip with audio volume multiplied by the
value `factor`. Can be applied to both audio and video clips.
This effect is loaded as a clip method when you use moviepy.editor,
so you can just write ``clip.multiply_volume(2)``
Examples
---------
>>> from moviepy import AudioFileClip
>>> music = AudioFileClip('music.ogg')
>>> new_clip = clip.multiply_volume(2) # doubles audio volume
>>> new_clip = clip.multiply_volume(0.5) # half audio
"""
return clip.transform(
lambda get_frame, t: factor * get_frame(t), keep_duration=True
)
20 changes: 0 additions & 20 deletions moviepy/audio/fx/volumex.py

This file was deleted.

47 changes: 45 additions & 2 deletions tests/test_fx.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from moviepy import AudioClip, AudioFileClip, BitmapClip, ColorClip, VideoFileClip
from moviepy.audio.fx import audio_normalize
from moviepy.audio.fx import multiply_stereo_volume
from moviepy.utils import close_all_clips
from moviepy.video.fx import (
blackwhite,
Expand Down Expand Up @@ -512,19 +513,61 @@ def test_time_symmetrize():
assert clip1 == target1


def test_normalize():
def test_audio_normalize():
clip = AudioFileClip("media/crunching.mp3")
clip = audio_normalize(clip)
assert clip.max_volume() == 1
close_all_clips(locals())


def test_normalize_muted():
def test_audio_normalize_muted():
z_array = np.array([0.0])
make_frame = lambda t: z_array
clip = AudioClip(make_frame, duration=1, fps=44100)
clip = audio_normalize(clip)
assert np.array_equal(clip.to_soundarray(), z_array)

close_all_clips(locals())


def test_multiply_stereo_volume():
clip = AudioFileClip("media/crunching.mp3")

# mute
clip_left_channel_muted = multiply_stereo_volume(clip, left=0)
clip_right_channel_muted = multiply_stereo_volume(clip, right=0, left=2)

left_channel_muted = clip_left_channel_muted.to_soundarray()[:, 0]
right_channel_muted = clip_right_channel_muted.to_soundarray()[:, 1]

z_channel = np.zeros(len(left_channel_muted))

assert np.array_equal(left_channel_muted, z_channel)
assert np.array_equal(right_channel_muted, z_channel)

# double level
left_channel_doubled = clip_right_channel_muted.to_soundarray()[:, 0]
d_channel = clip.to_soundarray()[:, 0] * 2
assert np.array_equal(left_channel_doubled, d_channel)

# mono muted
sinus_wave = lambda t: [np.sin(440 * 2 * np.pi * t)]
mono_clip = AudioClip(sinus_wave, duration=2, fps=22050)
muted_mono_clip = multiply_stereo_volume(mono_clip, left=0)
mono_channel_muted = muted_mono_clip.to_soundarray()

z_channel = np.zeros(len(mono_channel_muted))
assert np.array_equal(mono_channel_muted, z_channel)

# mono doubled
mono_clip = AudioClip(sinus_wave, duration=2, fps=22050)
doubled_mono_clip = multiply_stereo_volume(
mono_clip, left=None, right=2
) # using right
mono_channel_doubled = doubled_mono_clip.to_soundarray()
d_channel = mono_clip.to_soundarray() * 2
assert np.array_equal(mono_channel_doubled, d_channel)

close_all_clips(locals())


Expand Down

0 comments on commit 20309f1

Please sign in to comment.