Skip to content

back to Reference (Gold) summary

Reference (Gold): moviepy

Pytest Summary for test tests

status count
passed 108
failed 2
total 110
collected 110

Failed pytests:

test_PR.py::test_PR_424

test_PR.py::test_PR_424
def test_PR_424():
        """Ensure deprecation and user warnings are triggered."""
        import warnings
        warnings.simplefilter('always') # Alert us of deprecation warnings.

        # Recommended use
        ColorClip([1000, 600], color=(60, 60, 60), duration=10).close()

        with pytest.warns(DeprecationWarning):
            # Uses `col` so should work the same as above, but give warning.
            ColorClip([1000, 600], col=(60, 60, 60), duration=10).close()

        # Catch all warnings as record.
>       with pytest.warns(None) as record:

tests/test_PR.py:67: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = WarningsChecker(record=True), expected_warning = None, match_expr = None

    def __init__(
        self,
        expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning,
        match_expr: str | Pattern[str] | None = None,
        *,
        _ispytest: bool = False,
    ) -> None:
        check_ispytest(_ispytest)
        super().__init__(_ispytest=True)

        msg = "exceptions must be derived from Warning, not %s"
        if isinstance(expected_warning, tuple):
            for exc in expected_warning:
                if not issubclass(exc, Warning):
                    raise TypeError(msg % type(exc))
            expected_warning_tup = expected_warning
        elif isinstance(expected_warning, type) and issubclass(
            expected_warning, Warning
        ):
            expected_warning_tup = (expected_warning,)
        else:
>           raise TypeError(msg % type(expected_warning))
E           TypeError: exceptions must be derived from Warning, not 

.venv/lib/python3.10/site-packages/_pytest/recwarn.py:280: TypeError

test_tools.py::test_sys_write_flush

test_tools.py::test_sys_write_flush
def test_sys_write_flush():
        """Test for sys_write-flush function. Check that stdout has no content after flushing."""
        tools.sys_write_flush("hello world")

        file = sys.stdout.read()
>       assert file == b""
E       AssertionError: assert '' == b''

tests/test_tools.py:62: AssertionError

Patch diff

diff --git a/moviepy/Clip.py b/moviepy/Clip.py
index 8d3e1b0..24bc3b2 100644
--- a/moviepy/Clip.py
+++ b/moviepy/Clip.py
@@ -3,14 +3,20 @@ This module implements the central object of MoviePy, the Clip, and
 all the methods that are common to the two subclasses of Clip, VideoClip
 and AudioClip.
 """
+
 from copy import copy
+
 import numpy as np
 import proglog
 from tqdm import tqdm
-from moviepy.decorators import apply_to_audio, apply_to_mask, convert_to_seconds, outplace, requires_duration, use_clip_fps_by_default
+
+from moviepy.decorators import (apply_to_audio, apply_to_mask,
+                                convert_to_seconds, outplace,
+                                requires_duration, use_clip_fps_by_default)


 class Clip:
+
     """

      Base class of all clips (VideoClips and AudioClips).
@@ -32,12 +38,19 @@ class Clip:
        this case their duration will be ``None``.

      """
+   
+    # prefix for all temporary video and audio files.
+    # You can overwrite it with 
+    # >>> Clip._TEMP_FILES_PREFIX = "temp_"
+
     _TEMP_FILES_PREFIX = 'TEMP_MPY_'

     def __init__(self):
+
         self.start = 0
         self.end = None
         self.duration = None
+
         self.memoize = False
         self.memoized_t = None
         self.memoize_frame = None
@@ -52,7 +65,14 @@ class Clip:
         there is an outplace transformation of the clip (clip.resize,
         clip.subclip, etc.)
         """
-        pass
+
+        newclip = copy(self)
+        if hasattr(self, 'audio'):
+            newclip.audio = copy(self.audio)
+        if hasattr(self, 'mask'):
+            newclip.mask = copy(self.mask)
+            
+        return newclip

     @convert_to_seconds(['t'])
     def get_frame(self, t):
@@ -60,7 +80,17 @@ class Clip:
         Gets a numpy array representing the RGB picture of the clip at time t
         or (mono or stereo) value for a sound clip
         """
-        pass
+        # Coming soon: smart error handling for debugging at this point
+        if self.memoize:
+            if t == self.memoized_t:
+                return self.memoized_frame
+            else:
+                frame = self.make_frame(t)
+                self.memoized_t = t
+                self.memoized_frame = frame
+                return frame
+        else:
+            return self.make_frame(t)

     def fl(self, fun, apply_to=None, keep_duration=True):
         """ General processing of a clip.
@@ -99,7 +129,26 @@ class Clip:
         >>> newclip = clip.fl(fl, apply_to='mask')

         """
-        pass
+        if apply_to is None:
+            apply_to = []
+
+        #mf = copy(self.make_frame)
+        newclip = self.set_make_frame(lambda t: fun(self.get_frame, t))
+
+        if not keep_duration:
+            newclip.duration = None
+            newclip.end = None
+
+        if isinstance(apply_to, str):
+            apply_to = [apply_to]
+
+        for attr in apply_to:
+            a = getattr(newclip, attr, None) 
+            if a is not None:    
+                new_a = a.fl(fun, keep_duration=keep_duration)
+                setattr(newclip, attr, new_a)
+
+        return newclip

     def fl_time(self, t_func, apply_to=None, keep_duration=False):
         """
@@ -132,7 +181,11 @@ class Clip:
         >>> newclip = clip.fl_time(lambda: 3-t)

         """
-        pass
+        if apply_to is None:
+            apply_to = []
+
+        return self.fl(lambda gf, t: gf(t_func(t)), apply_to,
+                       keep_duration=keep_duration)

     def fx(self, func, *args, **kwargs):
         """
@@ -155,7 +208,10 @@ class Clip:
         >>> resize( volumex( mirrorx( clip ), 0.5), 0.3)

         """
-        pass
+
+        return func(self, *args, **kwargs)
+
+

     @apply_to_mask
     @apply_to_audio
@@ -179,7 +235,14 @@ class Clip:
         These changes are also applied to the ``audio`` and ``mask``
         clips of the current clip, if they exist.
         """
-        pass
+
+        self.start = t
+        if (self.duration is not None) and change_end:
+            self.end = t + self.duration
+        elif self.end is not None:
+            self.duration = self.end - self.start
+
+

     @apply_to_mask
     @apply_to_audio
@@ -193,7 +256,15 @@ class Clip:
         Also sets the duration of the mask and audio, if any,
         of the returned clip.
         """
-        pass
+        self.end = t
+        if self.end is None: return
+        if self.start is None:
+            if self.duration is not None:
+                self.start = max(0, t - newclip.duration)
+        else:
+            self.duration = self.end - self.start
+
+

     @apply_to_mask
     @apply_to_audio
@@ -210,7 +281,16 @@ class Clip:
         be modified in function of the duration and the preset end
         of the clip.
         """
-        pass
+        self.duration = t
+
+        if change_end:
+            self.end = None if (t is None) else (self.start + t)
+        else:
+            if self.duration is None:
+                raise Exception("Cannot change clip start when new"
+                                "duration is None")
+            self.start = self.end - t
+

     @outplace
     def set_make_frame(self, make_frame):
@@ -218,23 +298,24 @@ class Clip:
         Sets a ``make_frame`` attribute for the clip. Useful for setting
         arbitrary/complicated videoclips.
         """
-        pass
+        self.make_frame = make_frame

     @outplace
     def set_fps(self, fps):
         """ Returns a copy of the clip with a new default fps for functions like
         write_videofile, iterframe, etc. """
-        pass
+        self.fps = fps
+

     @outplace
     def set_ismask(self, ismask):
         """ Says wheter the clip is a mask or not (ismask is a boolean)"""
-        pass
+        self.ismask = ismask

     @outplace
     def set_memoize(self, memoize):
         """ Sets wheter the clip should keep the last frame read in memory """
-        pass
+        self.memoize = memoize

     @convert_to_seconds(['t'])
     def is_playing(self, t):
@@ -247,7 +328,28 @@ class Clip:
         theclip, else returns a vector [b_1, b_2, b_3...] where b_i
         is true iff tti is in the clip.
         """
-        pass
+
+        if isinstance(t, np.ndarray):
+            # is the whole list of t outside the clip ?
+            tmin, tmax = t.min(), t.max()
+
+            if (self.end is not None) and (tmin >= self.end):
+                return False
+
+            if tmax < self.start:
+                return False
+
+            # If we arrive here, a part of t falls in the clip
+            result = 1 * (t >= self.start)
+            if self.end is not None:
+                result *= (t <= self.end)
+            return result
+
+        else:
+
+            return((t >= self.start) and
+                   ((self.end is None) or (t < self.end)))
+

     @convert_to_seconds(['t_start', 't_end'])
     @apply_to_mask
@@ -273,7 +375,41 @@ class Clip:
         subclips of ``mask`` and ``audio`` the original clip, if
         they exist.
         """
-        pass
+
+        if t_start < 0:
+            # Make this more Python-like, a negative value means to move
+            # backward from the end of the clip
+            t_start = self.duration + t_start   # Remember t_start is negative
+
+        if (self.duration is not None) and (t_start > self.duration):
+            raise ValueError("t_start (%.02f) " % t_start +
+                             "should be smaller than the clip's " +
+                             "duration (%.02f)." % self.duration)
+
+        newclip = self.fl_time(lambda t: t + t_start, apply_to=[])
+
+        if (t_end is None) and (self.duration is not None):
+
+            t_end = self.duration
+
+        elif (t_end is not None) and (t_end < 0):
+
+            if self.duration is None:
+
+                print("Error: subclip with negative times (here %s)" % (str((t_start, t_end)))
+                      + " can only be extracted from clips with a ``duration``")
+
+            else:
+
+                t_end = self.duration + t_end
+
+        if t_end is not None:
+
+            newclip.duration = t_end - t_start
+            newclip.end = newclip.start + newclip.duration
+
+        return newclip
+

     @apply_to_mask
     @apply_to_audio
@@ -291,11 +427,22 @@ class Clip:
         The resulting clip's ``audio`` and ``mask`` will also be cutout
         if they exist.
         """
-        pass
+
+        fl = lambda t: t + (t >= ta)*(tb - ta)
+        newclip = self.fl_time(fl)
+
+        if self.duration is not None:
+
+            return newclip.set_duration(self.duration - (tb - ta))
+
+        else:
+
+            return newclip

     @requires_duration
     @use_clip_fps_by_default
-    def iter_frames(self, fps=None, with_times=False, logger=None, dtype=None):
+    def iter_frames(self, fps=None, with_times = False, logger=None,
+                    dtype=None):
         """ Iterates over all the frames of the clip.

         Returns each frame of the clip as a HxWxN np.array,
@@ -320,14 +467,33 @@ class Clip:
         >>> print ( [frame[0,:,0].max()
                      for frame in myclip.iter_frames()])
         """
-        pass
+        logger = proglog.default_bar_logger(logger)
+        for t in logger.iter_bar(t=np.arange(0, self.duration, 1.0/fps)):
+            frame = self.get_frame(t)
+            if (dtype is not None) and (frame.dtype != dtype):
+                frame = frame.astype(dtype)
+            if with_times:
+                yield t, frame
+            else:
+                yield frame

     def close(self):
         """ 
             Release any resources that are in use.
         """
+
+        #    Implementation note for subclasses:
+        #
+        #    * Memory-based resources can be left to the garbage-collector.
+        #    * However, any open files should be closed, and subprocesses
+        #      should be terminated.
+        #    * Be wary that shallow copies are frequently used.
+        #      Closing a Clip may affect its copies.
+        #    * Therefore, should NOT be called by __del__().
         pass

+    # Support the Context Manager protocol, to ensure that resources are cleaned up.
+
     def __enter__(self):
         return self

diff --git a/moviepy/audio/AudioClip.py b/moviepy/audio/AudioClip.py
index 350c197..122d9b2 100644
--- a/moviepy/audio/AudioClip.py
+++ b/moviepy/audio/AudioClip.py
@@ -1,7 +1,9 @@
 import os
+
 import numpy as np
 import proglog
 from tqdm import tqdm
+
 from moviepy.audio.io.ffmpeg_audiowriter import ffmpeg_audiowrite
 from moviepy.Clip import Clip
 from moviepy.decorators import requires_duration
@@ -41,11 +43,13 @@ class AudioClip(Clip):
     >>> clip.preview()

     """
-
+    
     def __init__(self, make_frame=None, duration=None, fps=None):
         Clip.__init__(self)
+
         if fps is not None:
             self.fps = fps
+
         if make_frame is not None:
             self.make_frame = make_frame
             frame0 = self.get_frame(0)
@@ -56,17 +60,33 @@ class AudioClip(Clip):
         if duration is not None:
             self.duration = duration
             self.end = duration
-
+    
     @requires_duration
     def iter_chunks(self, chunksize=None, chunk_duration=None, fps=None,
-        quantize=False, nbytes=2, logger=None):
+                    quantize=False, nbytes=2, logger=None):
         """ Iterator that returns the whole sound array of the clip by chunks
         """
-        pass
+        if fps is None:
+            fps = self.fps
+        logger = proglog.default_bar_logger(logger)
+        if chunk_duration is not None:
+            chunksize = int(chunk_duration*fps)
+        
+        totalsize = int(fps*self.duration)
+
+        nchunks = totalsize // chunksize + 1
+
+        pospos = np.linspace(0, totalsize, nchunks + 1, endpoint=True, dtype=int)
+        
+        for i in logger.iter_bar(chunk=list(range(nchunks))):
+            size = pospos[i+1] - pospos[i]
+            assert(size <= chunksize)
+            tt = (1.0/fps)*np.arange(pospos[i], pospos[i+1])
+            yield self.to_soundarray(tt, nbytes=nbytes, quantize=quantize,
+                                        fps=fps, buffersize=chunksize)

     @requires_duration
-    def to_soundarray(self, tt=None, fps=None, quantize=False, nbytes=2,
-        buffersize=50000):
+    def to_soundarray(self, tt=None, fps=None, quantize=False, nbytes=2, buffersize=50000):
         """
         Transforms the sound into an array that can be played by pygame
         or written in a wav file. See ``AudioClip.preview``.
@@ -83,12 +103,49 @@ class AudioClip(Clip):
           2 for 16bit, 4 for 32bit sound.

         """
-        pass
+        if fps is None:
+            fps = self.fps
+       
+        stacker = np.vstack if self.nchannels == 2 else np.hstack
+        max_duration = 1.0 * buffersize / fps
+        if tt is None:
+            if self.duration > max_duration:
+                return stacker(self.iter_chunks(fps=fps, quantize=quantize,
+                                                nbytes=2, chunksize=buffersize))
+            else:
+                tt = np.arange(0, self.duration, 1.0/fps)
+        """
+        elif len(tt)> 1.5*buffersize:
+            nchunks = int(len(tt)/buffersize+1)
+            tt_chunks = np.array_split(tt, nchunks)
+            return stacker([self.to_soundarray(tt=ttc, buffersize=buffersize, fps=fps,
+                                        quantize=quantize, nbytes=nbytes)
+                              for ttc in tt_chunks])
+        """
+        #print tt.max() - tt.min(), tt.min(), tt.max()
+        
+        snd_array = self.get_frame(tt)
+
+        if quantize:
+            snd_array = np.maximum(-0.99, np.minimum(0.99, snd_array))
+            inttype = {1: 'int8', 2: 'int16', 4: 'int32'}[nbytes]
+            snd_array = (2**(8*nbytes-1)*snd_array).astype(inttype)
+        
+        return snd_array
+
+    def max_volume(self, stereo=False, chunksize=50000, logger=None):
+        
+        stereo = stereo and (self.nchannels == 2)
+
+        maxi = np.array([0, 0]) if stereo else 0
+        for chunk in self.iter_chunks(chunksize=chunksize,logger=logger):
+            maxi = np.maximum(maxi, abs(chunk).max(axis=0)) if stereo else max(maxi, abs(chunk).max())
+        return maxi

     @requires_duration
     def write_audiofile(self, filename, fps=None, nbytes=2, buffersize=2000,
-        codec=None, bitrate=None, ffmpeg_params=None, write_logfile=False,
-        verbose=True, logger='bar'):
+                        codec=None, bitrate=None, ffmpeg_params=None,
+                        write_logfile=False, verbose=True, logger='bar'):
         """ Writes an audio file from the AudioClip.


@@ -131,11 +188,32 @@ class AudioClip(Clip):
           Either 'bar' or None or any Proglog logger

         """
-        pass
+        if not fps:
+            if not self.fps:
+                fps = 44100
+            else:
+                fps = self.fps
+
+        if codec is None:
+            name, ext = os.path.splitext(os.path.basename(filename))
+            try:
+                codec = extensions_dict[ext[1:]]['codec'][0]
+            except KeyError:
+                raise ValueError("MoviePy couldn't find the codec associated "
+                                 "with the filename. Provide the 'codec' "
+                                 "parameter in write_audiofile.")
+
+        return ffmpeg_audiowrite(self, filename, fps, nbytes, buffersize,
+                                 codec=codec, bitrate=bitrate,
+                                 write_logfile=write_logfile, verbose=verbose,
+                                 ffmpeg_params=ffmpeg_params,
+                                 logger=logger)


+# The to_audiofile method is replaced by the more explicit write_audiofile.
 AudioClip.to_audiofile = deprecated_version_of(AudioClip.write_audiofile,
-    'to_audiofile')
+                                               'to_audiofile')
+###


 class AudioArrayClip(AudioClip):
@@ -155,8 +233,9 @@ class AudioArrayClip(AudioClip):
       played.

     """
-
+    
     def __init__(self, array, fps):
+        
         Clip.__init__(self)
         self.array = array
         self.fps = fps
@@ -165,8 +244,9 @@ class AudioArrayClip(AudioClip):
         def make_frame(t):
             """ complicated, but must be able to handle the case where t
             is a list of the form sin(t) """
+            
             if isinstance(t, np.ndarray):
-                array_inds = (self.fps * t).astype(int)
+                array_inds = (self.fps*t).astype(int)
                 in_array = (array_inds > 0) & (array_inds < len(self.array))
                 result = np.zeros((len(t), 2))
                 result[in_array] = self.array[array_inds[in_array]]
@@ -174,14 +254,16 @@ class AudioArrayClip(AudioClip):
             else:
                 i = int(self.fps * t)
                 if i < 0 or i >= len(self.array):
-                    return 0 * self.array[0]
+                    return 0*self.array[0]
                 else:
                     return self.array[i]
+
         self.make_frame = make_frame
         self.nchannels = len(list(self.get_frame(0)))
-
-
+        
+        
 class CompositeAudioClip(AudioClip):
+
     """ Clip made by composing several AudioClips.

     An audio clip made by putting together several audio clips.
@@ -197,8 +279,10 @@ class CompositeAudioClip(AudioClip):
     """

     def __init__(self, clips):
+
         Clip.__init__(self)
         self.clips = clips
+        
         ends = [c.end for c in self.clips]
         self.nchannels = max([c.nchannels for c in self.clips])
         if not any([(e is None) for e in ends]):
@@ -206,14 +290,21 @@ class CompositeAudioClip(AudioClip):
             self.end = max(ends)

         def make_frame(t):
+            
             played_parts = [c.is_playing(t) for c in self.clips]
-            sounds = [(c.get_frame(t - c.start) * np.array([part]).T) for c,
-                part in zip(self.clips, played_parts) if part is not False]
+            
+            sounds = [c.get_frame(t - c.start)*np.array([part]).T
+                      for c, part in zip(self.clips, played_parts)
+                      if (part is not False)]
+                     
             if isinstance(t, np.ndarray):
                 zero = np.zeros((len(t), self.nchannels))
+                
             else:
                 zero = np.zeros(self.nchannels)
+                
             return zero + sum(sounds)
+
         self.make_frame = make_frame


@@ -221,4 +312,12 @@ def concatenate_audioclips(clips):
     """
     The clip with the highest FPS will be the FPS of the result clip.
     """
-    pass
+    durations = [c.duration for c in clips]
+    tt = np.cumsum([0]+durations)  # start times, and end time.
+    newclips = [c.set_start(t) for c, t in zip(clips, tt)]
+
+    result = CompositeAudioClip(newclips).set_duration(tt[-1])
+
+    fpss = [c.fps for c in clips if getattr(c, 'fps', None)]
+    result.fps = max(fpss) if fpss else None
+    return result
diff --git a/moviepy/audio/fx/audio_fadein.py b/moviepy/audio/fx/audio_fadein.py
index d7d564e..20b1eed 100644
--- a/moviepy/audio/fx/audio_fadein.py
+++ b/moviepy/audio/fx/audio_fadein.py
@@ -1,4 +1,5 @@
 import numpy as np
+
 from moviepy.decorators import audio_video_fx


@@ -6,4 +7,15 @@ from moviepy.decorators import audio_video_fx
 def audio_fadein(clip, duration):
     """ Return an audio (or video) clip that is first mute, then the
         sound arrives progressively over ``duration`` seconds. """
-    pass
+        
+    def fading(gf,t):
+        gft = gf(t)
+        
+        if np.isscalar(t):
+            factor = min(1.0 * t / duration, 1)
+            factor = np.array([factor,factor])
+        else:
+            factor = np.minimum(1.0 * t / duration, 1)
+            factor = np.vstack([factor,factor]).T
+        return factor * gft
+    return clip.fl(fading, keep_duration = True)
diff --git a/moviepy/audio/fx/audio_fadeout.py b/moviepy/audio/fx/audio_fadeout.py
index 557d1aa..ab6f40e 100644
--- a/moviepy/audio/fx/audio_fadeout.py
+++ b/moviepy/audio/fx/audio_fadeout.py
@@ -1,4 +1,5 @@
 import numpy as np
+
 from moviepy.decorators import audio_video_fx, requires_duration


@@ -7,4 +8,16 @@ from moviepy.decorators import audio_video_fx, requires_duration
 def audio_fadeout(clip, duration):
     """ Return a sound clip where the sound fades out progressively
         over ``duration`` seconds at the end of the clip. """
-    pass
+    
+    def fading(gf,t):
+        gft = gf(t)
+        
+        if np.isscalar(t):
+            factor = min(1.0 * (clip.duration - t) / duration, 1)
+            factor = np.array([factor,factor])
+        else:
+            factor = np.minimum( 1.0 * (clip.duration - t) / duration, 1)
+            factor = np.vstack([factor,factor]).T
+        return factor * gft
+    
+    return clip.fl(fading, keep_duration = True)
diff --git a/moviepy/audio/fx/audio_left_right.py b/moviepy/audio/fx/audio_left_right.py
index b09d9cc..c98abcc 100644
--- a/moviepy/audio/fx/audio_left_right.py
+++ b/moviepy/audio/fx/audio_left_right.py
@@ -11,4 +11,5 @@ def audio_left_right(audioclip, left=1, right=1, merge=False):
     Makes a stereo audio clip in which the volume of left and right
     is controllable
     """
-    pass
+    funleft = (lambda t: left) if np.isscalar(left) else left
+    funright = (lambda t: right) if np.isscalar(right) else right
diff --git a/moviepy/audio/fx/audio_loop.py b/moviepy/audio/fx/audio_loop.py
index cb07e52..7e309b7 100644
--- a/moviepy/audio/fx/audio_loop.py
+++ b/moviepy/audio/fx/audio_loop.py
@@ -17,4 +17,12 @@ def audio_loop(audioclip, nloops=None, duration=None):
     >>> videoclip.set_audio(audio)

     """
-    pass
+
+    if duration is not None:
+
+        nloops = int( duration/ audioclip.duration)+1
+        return concatenate_audioclips(nloops*[audioclip]).set_duration(duration)
+    
+    else:
+
+        return concatenate_audioclips(nloops*[audioclip])
diff --git a/moviepy/audio/fx/audio_normalize.py b/moviepy/audio/fx/audio_normalize.py
index 7849aa6..0c4daff 100644
--- a/moviepy/audio/fx/audio_normalize.py
+++ b/moviepy/audio/fx/audio_normalize.py
@@ -1,4 +1,5 @@
 from moviepy.decorators import audio_video_fx
+
 from .volumex import volumex


@@ -16,4 +17,7 @@ def audio_normalize(clip):
     >>> videoclip = VideoFileClip('myvideo.mp4').fx(afx.audio_normalize)

     """
-    pass
+
+
+    mv = clip.max_volume()
+    return volumex(clip, 1 / mv)
diff --git a/moviepy/audio/fx/volumex.py b/moviepy/audio/fx/volumex.py
index 573b48a..6ec84ad 100644
--- a/moviepy/audio/fx/volumex.py
+++ b/moviepy/audio/fx/volumex.py
@@ -16,4 +16,5 @@ def volumex(clip, factor):
     >>> newclip = clip.fx( volumex, 0.5) # half audio, use with fx
     >>> newclip = clip.volumex(2) # only if you used "moviepy.editor"
     """
-    pass
+    return clip.fl(lambda gf, t: factor * gf(t),
+                   keep_duration=True)
diff --git a/moviepy/audio/io/AudioFileClip.py b/moviepy/audio/io/AudioFileClip.py
index 4c0ca37..b79ab23 100644
--- a/moviepy/audio/io/AudioFileClip.py
+++ b/moviepy/audio/io/AudioFileClip.py
@@ -1,9 +1,11 @@
 from __future__ import division
+
 from moviepy.audio.AudioClip import AudioClip
 from moviepy.audio.io.readers import FFMPEG_AudioReader


 class AudioFileClip(AudioClip):
+
     """
     An audio clip read from a sound file, or an array.
     The whole file is not loaded in memory. Instead, only a portion is
@@ -61,14 +63,17 @@ class AudioFileClip(AudioClip):
     """

     def __init__(self, filename, buffersize=200000, nbytes=2, fps=44100):
+
         AudioClip.__init__(self)
+
         self.filename = filename
         self.reader = FFMPEG_AudioReader(filename, fps=fps, nbytes=nbytes,
-            buffersize=buffersize)
+                                         buffersize=buffersize)
         self.fps = fps
         self.duration = self.reader.duration
         self.end = self.reader.duration
         self.buffersize = self.reader.buffersize
+
         self.make_frame = lambda t: self.reader.get_frame(t)
         self.nchannels = self.reader.nchannels

@@ -76,8 +81,10 @@ class AudioFileClip(AudioClip):
         """ Returns a copy of the AudioFileClip, i.e. a new entrance point
             to the audio file. Use copy when you have different clips
             watching the audio file at different times. """
-        pass
+        return AudioFileClip(self.filename, self.buffersize)

     def close(self):
         """ Close the internal reader. """
-        pass
+        if self.reader:
+            self.reader.close_proc()
+            self.reader = None
diff --git a/moviepy/audio/io/ffmpeg_audiowriter.py b/moviepy/audio/io/ffmpeg_audiowriter.py
index 9501306..d02649f 100644
--- a/moviepy/audio/io/ffmpeg_audiowriter.py
+++ b/moviepy/audio/io/ffmpeg_audiowriter.py
@@ -1,6 +1,8 @@
 import os
 import subprocess as sp
+
 import proglog
+
 from moviepy.compat import DEVNULL
 from moviepy.config import get_setting
 from moviepy.decorators import requires_duration
@@ -32,29 +34,105 @@ class FFMPEG_AudioWriter:

     """

-    def __init__(self, filename, fps_input, nbytes=2, nchannels=2, codec=
-        'libfdk_aac', bitrate=None, input_video=None, logfile=None,
-        ffmpeg_params=None):
+    def __init__(self, filename, fps_input, nbytes=2,
+                 nchannels=2, codec='libfdk_aac', bitrate=None,
+                 input_video=None, logfile=None, ffmpeg_params=None):
+
         self.filename = filename
         self.codec = codec
+
         if logfile is None:
             logfile = sp.PIPE
-        cmd = [get_setting('FFMPEG_BINARY'), '-y', '-loglevel', 'error' if 
-            logfile == sp.PIPE else 'info', '-f', 's%dle' % (8 * nbytes),
-            '-acodec', 'pcm_s%dle' % (8 * nbytes), '-ar', '%d' % fps_input,
-            '-ac', '%d' % nchannels, '-i', '-'] + (['-vn'] if input_video is
-            None else ['-i', input_video, '-vcodec', 'copy']) + ['-acodec',
-            codec] + ['-ar', '%d' % fps_input] + ['-strict', '-2'] + ([
-            '-ab', bitrate] if bitrate is not None else []) + (ffmpeg_params if
-            ffmpeg_params else []) + [filename]
-        popen_params = {'stdout': DEVNULL, 'stderr': logfile, 'stdin': sp.PIPE}
-        if os.name == 'nt':
-            popen_params['creationflags'] = 134217728
+
+        cmd = ([get_setting("FFMPEG_BINARY"), '-y',
+                "-loglevel", "error" if logfile == sp.PIPE else "info",
+                "-f", 's%dle' % (8*nbytes),
+                "-acodec",'pcm_s%dle' % (8*nbytes),
+                '-ar', "%d" % fps_input,
+                '-ac', "%d" % nchannels,
+                '-i', '-']
+               + (['-vn'] if input_video is None else ["-i", input_video, '-vcodec', 'copy'])
+               + ['-acodec', codec]
+               + ['-ar', "%d" % fps_input]
+               + ['-strict', '-2']  # needed to support codec 'aac'
+               + (['-ab', bitrate] if (bitrate is not None) else [])
+               + (ffmpeg_params if ffmpeg_params else [])
+               + [filename])
+
+        popen_params = {"stdout": DEVNULL,
+                        "stderr": logfile,
+                        "stdin": sp.PIPE}
+
+        if os.name == "nt":
+            popen_params["creationflags"] = 0x08000000
+
         self.proc = sp.Popen(cmd, **popen_params)

+    def write_frames(self, frames_array):
+        try:
+            try:
+                self.proc.stdin.write(frames_array.tobytes())
+            except NameError:
+                self.proc.stdin.write(frames_array.tostring())
+        except IOError as err:
+            ffmpeg_error = self.proc.stderr.read()
+            error = (str(err) + ("\n\nMoviePy error: FFMPEG encountered "
+                                 "the following error while writing file %s:" % self.filename
+                                 + "\n\n" + str(ffmpeg_error)))
+
+            if b"Unknown encoder" in ffmpeg_error:
+
+                error = (error +
+                         ("\n\nThe audio export failed because FFMPEG didn't "
+                          "find the specified codec for audio encoding (%s). "
+                          "Please install this codec or change the codec when "
+                          "calling to_videofile or to_audiofile. For instance "
+                          "for mp3:\n"
+                          "   >>> to_videofile('myvid.mp4', audio_codec='libmp3lame')"
+                          ) % (self.codec))
+
+            elif b"incorrect codec parameters ?" in ffmpeg_error:
+
+                error = (error +
+                         ("\n\nThe audio export failed, possibly because the "
+                          "codec specified for the video (%s) is not compatible"
+                          " with the given extension (%s). Please specify a "
+                          "valid 'codec' argument in to_videofile. This would "
+                          "be 'libmp3lame' for mp3, 'libvorbis' for ogg...")
+                         % (self.codec, self.ext))
+
+            elif b"encoder setup failed" in ffmpeg_error:
+
+                error = (error +
+                         ("\n\nThe audio export failed, possily because the "
+                          "bitrate you specified was two high or too low for "
+                          "the video codec."))
+
+            else:
+                error = (error +
+                         ("\n\nIn case it helps, make sure you are using a "
+                          "recent version of FFMPEG (the versions in the "
+                          "Ubuntu/Debian repos are deprecated)."))
+
+            raise IOError(error)
+
+    def close(self):
+        if hasattr(self, 'proc') and self.proc:
+            self.proc.stdin.close()
+            self.proc.stdin = None
+            if self.proc.stderr is not None:
+                self.proc.stderr.close()
+                self.proc.stdee = None
+            # If this causes deadlocks, consider terminating instead.
+            self.proc.wait()
+            self.proc = None
+
     def __del__(self):
+        # If the garbage collector comes, make sure the subprocess is terminated.
         self.close()

+    # Support the Context Manager protocol, to ensure that resources are cleaned up.
+
     def __enter__(self):
         return self

@@ -63,13 +141,36 @@ class FFMPEG_AudioWriter:


 @requires_duration
-def ffmpeg_audiowrite(clip, filename, fps, nbytes, buffersize, codec=
-    'libvorbis', bitrate=None, write_logfile=False, verbose=True,
-    ffmpeg_params=None, logger='bar'):
+def ffmpeg_audiowrite(clip, filename, fps, nbytes, buffersize,
+                      codec='libvorbis', bitrate=None,
+                      write_logfile=False, verbose=True,
+                      ffmpeg_params=None, logger='bar'):
     """
     A function that wraps the FFMPEG_AudioWriter to write an AudioClip
     to a file.

     NOTE: verbose is deprecated.
     """
-    pass
+
+    if write_logfile:
+        logfile = open(filename + ".log", 'w+')
+    else:
+        logfile = None
+    logger = proglog.default_bar_logger(logger)
+    logger(message="MoviePy - Writing audio in %s" % filename)
+    writer = FFMPEG_AudioWriter(filename, fps, nbytes, clip.nchannels,
+                                codec=codec, bitrate=bitrate,
+                                logfile=logfile,
+                                ffmpeg_params=ffmpeg_params)
+
+    for chunk in clip.iter_chunks(chunksize=buffersize,
+                                  quantize=True,
+                                  nbytes=nbytes, fps=fps,
+                                  logger=logger):
+        writer.write_frames(chunk)
+
+    writer.close()
+
+    if write_logfile:
+        logfile.close()
+    logger(message="MoviePy - Done.")
diff --git a/moviepy/audio/io/preview.py b/moviepy/audio/io/preview.py
index d8a5730..e321b29 100644
--- a/moviepy/audio/io/preview.py
+++ b/moviepy/audio/io/preview.py
@@ -1,14 +1,17 @@
 import time
+
 import numpy as np
+
 import pygame as pg
 from moviepy.decorators import requires_duration
+
 pg.init()
 pg.display.set_caption('MoviePy')


 @requires_duration
-def preview(clip, fps=22050, buffersize=4000, nbytes=2, audioFlag=None,
-    videoFlag=None):
+def preview(clip, fps=22050,  buffersize=4000, nbytes=2, audioFlag=None,
+            videoFlag=None):
     """
     Plays the sound clip with pygame.

@@ -36,4 +39,30 @@ def preview(clip, fps=22050, buffersize=4000, nbytes=2, audioFlag=None,
       video and audio during ``VideoClip.preview()``.

     """
-    pass
+                 
+    pg.mixer.quit()
+    
+    pg.mixer.init(fps, -8 * nbytes, clip.nchannels, 1024)
+    totalsize = int(fps*clip.duration)
+    pospos = np.array(list(range(0, totalsize,  buffersize))+[totalsize])
+    tt = (1.0/fps)*np.arange(pospos[0], pospos[1])
+    sndarray = clip.to_soundarray(tt, nbytes=nbytes, quantize=True)
+    chunk = pg.sndarray.make_sound(sndarray)
+    
+    if (audioFlag is not None) and (videoFlag is not None):
+        audioFlag.set()
+        videoFlag.wait()
+        
+    channel = chunk.play()
+    for i in range(1, len(pospos)-1):
+        tt = (1.0/fps)*np.arange(pospos[i], pospos[i+1])
+        sndarray = clip.to_soundarray(tt, nbytes=nbytes, quantize=True)
+        chunk = pg.sndarray.make_sound(sndarray)
+        while channel.get_queue():
+            time.sleep(0.003)
+            if videoFlag is not None:
+                if not videoFlag.is_set():
+                    channel.stop()
+                    del channel
+                    return
+        channel.queue(chunk)
diff --git a/moviepy/audio/io/readers.py b/moviepy/audio/io/readers.py
index 2e9cb30..991e60b 100644
--- a/moviepy/audio/io/readers.py
+++ b/moviepy/audio/io/readers.py
@@ -1,7 +1,9 @@
 import os
 import subprocess as sp
 import warnings
+
 import numpy as np
+
 from moviepy.compat import DEVNULL, PY3
 from moviepy.config import get_setting
 from moviepy.video.io.ffmpeg_reader import ffmpeg_parse_infos
@@ -37,13 +39,14 @@ class FFMPEG_AudioReader:

     """

-    def __init__(self, filename, buffersize, print_infos=False, fps=44100,
-        nbytes=2, nchannels=2):
+    def __init__(self, filename, buffersize, print_infos=False,
+                 fps=44100, nbytes=2, nchannels=2):
+
         self.filename = filename
         self.nbytes = nbytes
         self.fps = fps
-        self.f = 's%dle' % (8 * nbytes)
-        self.acodec = 'pcm_s%dle' % (8 * nbytes)
+        self.f = 's%dle'%(8*nbytes)
+        self.acodec = 'pcm_s%dle'%(8*nbytes)
         self.nchannels = nchannels
         infos = ffmpeg_parse_infos(filename)
         self.duration = infos['duration']
@@ -53,18 +56,76 @@ class FFMPEG_AudioReader:
             self.duration = infos['duration']
         self.infos = infos
         self.proc = None
+
         self.nframes = int(self.fps * self.duration)
-        self.buffersize = min(self.nframes + 1, buffersize)
-        self.buffer = None
+        self.buffersize= min( self.nframes+1, buffersize )
+        self.buffer= None
         self.buffer_startframe = 1
         self.initialize()
         self.buffer_around(1)

-    def initialize(self, starttime=0):
+    def initialize(self, starttime = 0):
         """ Opens the file, creates the pipe. """
-        pass

-    def seek(self, pos):
+        self.close_proc() # if any
+
+        if starttime !=0 :
+            offset = min(1,starttime)
+            i_arg = ["-ss", "%.05f"%(starttime-offset),
+                    '-i', self.filename, '-vn',
+                    "-ss", "%.05f"%offset]
+        else:
+            i_arg = [ '-i', self.filename,  '-vn']
+
+
+        cmd = ([get_setting("FFMPEG_BINARY")] + i_arg +
+               [ '-loglevel', 'error',
+                 '-f', self.f,
+                '-acodec', self.acodec,
+                '-ar', "%d"%self.fps,
+                '-ac', '%d'%self.nchannels, '-'])
+
+        popen_params = {"bufsize": self.buffersize,
+                        "stdout": sp.PIPE,
+                        "stderr": sp.PIPE,
+                        "stdin": DEVNULL}
+
+        if os.name == "nt":
+            popen_params["creationflags"] = 0x08000000
+
+        self.proc = sp.Popen( cmd, **popen_params)
+
+        self.pos = np.round(self.fps*starttime)
+
+
+
+    def skip_chunk(self,chunksize):
+        s = self.proc.stdout.read(self.nchannels*chunksize*self.nbytes)
+        self.proc.stdout.flush()
+        self.pos = self.pos+chunksize
+
+
+
+    def read_chunk(self,chunksize):
+        # chunksize is not being autoconverted from float to int
+        chunksize = int(round(chunksize))
+        L = self.nchannels*chunksize*self.nbytes
+        s = self.proc.stdout.read(L)
+        dt = {1: 'int8',2:'int16',4:'int32'}[self.nbytes]
+        if hasattr(np, 'frombuffer'):
+            result = np.frombuffer(s, dtype=dt)
+        else:
+            result = np.fromstring(s, dtype=dt)
+        result = (1.0*result / 2**(8*self.nbytes-1)).\
+                                 reshape((int(len(result)/self.nchannels),
+                                          self.nchannels))
+        #self.proc.stdout.flush()
+        self.pos = self.pos+chunksize
+        return result
+
+
+
+    def seek(self,pos):
         """
         Reads a frame at time t. Note for coders: getting an arbitrary
         frame in the video with ffmpeg can be painfully slow if some
@@ -72,14 +133,122 @@ class FFMPEG_AudioReader:
         arbitrary frames whenever possible, by moving between adjacent
         frames.
         """
-        pass
+        if (pos < self.pos) or (pos> (self.pos+1000000)):
+            t = 1.0*pos/self.fps
+            self.initialize(t)
+        elif pos > self.pos:
+            #print pos
+            self.skip_chunk(pos-self.pos)
+        # last case standing: pos = current pos
+        self.pos = pos
+
+

-    def buffer_around(self, framenumber):
+    def close_proc(self):
+        if hasattr(self, 'proc') and self.proc is not None:
+            self.proc.terminate()
+            for std in [ self.proc.stdout,
+                         self.proc.stderr]:
+                std.close()
+            self.proc.wait()
+            self.proc = None
+
+    def get_frame(self, tt):
+
+        buffersize = self.buffersize
+        if isinstance(tt,np.ndarray):
+            # lazy implementation, but should not cause problems in
+            # 99.99 %  of the cases
+
+
+            # elements of t that are actually in the range of the
+            # audio file.
+            in_time = (tt>=0) & (tt < self.duration)
+
+        # Check that the requested time is in the valid range
+            if not in_time.any():
+                raise IOError("Error in file %s, "%(self.filename)+
+                       "Accessing time t=%.02f-%.02f seconds, "%(tt[0], tt[-1])+
+                       "with clip duration=%d seconds, "%self.duration)
+
+            # The np.round in the next line is super-important.
+            # Removing it results in artifacts in the noise.
+            frames = np.round((self.fps*tt)).astype(int)[in_time]
+            fr_min, fr_max = frames.min(), frames.max()
+
+            if not (0 <=
+                     (fr_min - self.buffer_startframe)
+                          < len(self.buffer)):
+                self.buffer_around(fr_min)
+            elif not (0 <=
+                        (fr_max - self.buffer_startframe)
+                             < len(self.buffer)):
+                self.buffer_around(fr_max)
+
+            try:
+                result = np.zeros((len(tt),self.nchannels))
+                indices = frames - self.buffer_startframe
+                if len(self.buffer) < self.buffersize // 2:
+                    indices = indices - (self.buffersize // 2 - len(self.buffer) + 1)
+                result[in_time] = self.buffer[indices]
+                return result
+
+            except IndexError as error:
+                warnings.warn("Error in file %s, "%(self.filename)+
+                       "At time t=%.02f-%.02f seconds, "%(tt[0], tt[-1])+
+                       "indices wanted: %d-%d, "%(indices.min(), indices.max())+
+                       "but len(buffer)=%d\n"%(len(self.buffer))+ str(error),
+                   UserWarning)
+
+                # repeat the last frame instead
+                indices[indices>=len(self.buffer)] = len(self.buffer) -1
+                result[in_time] = self.buffer[indices]
+                return result
+
+        else:
+
+            ind = int(self.fps*tt)
+            if ind<0 or ind> self.nframes: # out of time: return 0
+                return np.zeros(self.nchannels)
+
+            if not (0 <= (ind - self.buffer_startframe) <len(self.buffer)):
+                # out of the buffer: recenter the buffer
+                self.buffer_around(ind)
+
+            # read the frame in the buffer
+            return self.buffer[ind - self.buffer_startframe]
+
+
+    def buffer_around(self,framenumber):
         """
         Fills the buffer with frames, centered on ``framenumber``
         if possible
         """
-        pass
+
+        # start-frame for the buffer
+        new_bufferstart = max(0,  framenumber - self.buffersize // 2)
+
+
+        if (self.buffer is not None):
+            current_f_end  = self.buffer_startframe + self.buffersize
+            if (new_bufferstart <
+                        current_f_end  <
+                               new_bufferstart + self.buffersize):
+                # We already have one bit of what must be read
+                conserved = current_f_end - new_bufferstart + 1
+                chunksize = self.buffersize-conserved
+                array = self.read_chunk(chunksize)
+                self.buffer = np.vstack([self.buffer[-conserved:], array])
+            else:
+                self.seek(new_bufferstart)
+                self.buffer =  self.read_chunk(self.buffersize)
+        else:
+            self.seek(new_bufferstart)
+            self.buffer =  self.read_chunk(self.buffersize)
+
+        self.buffer_startframe = new_bufferstart
+

     def __del__(self):
+        # If the garbage collector comes, make sure the subprocess is terminated.
         self.close_proc()
diff --git a/moviepy/audio/tools/cuts.py b/moviepy/audio/tools/cuts.py
index e057157..3adedbb 100644
--- a/moviepy/audio/tools/cuts.py
+++ b/moviepy/audio/tools/cuts.py
@@ -1,7 +1,8 @@
+
 import numpy as np


-def find_audio_period(aclip, t_min=0.1, t_max=2, t_res=0.01):
+def find_audio_period(aclip, t_min=.1, t_max=2, t_res=.01):
     """ Finds the period, in seconds of an audioclip.

     The beat is then given by bpm = 60/T
@@ -9,4 +10,13 @@ def find_audio_period(aclip, t_min=0.1, t_max=2, t_res=0.01):
     t_min and _tmax are bounds for the returned value, t_res
     is the numerical precision
     """
-    pass
+    chunksize = int(t_res*aclip.fps)
+    chunk_duration = 1.0*chunksize/aclip.fps
+    # v denotes the list of volumes
+    v = np.array([(c**2).sum() for c in
+                aclip.iter_chunks(chunksize)])
+    v = v-v.mean()
+    corrs = np.correlate(v, v, mode = 'full')[-len(v):]
+    corrs[:int(t_min/chunk_duration)]=0
+    corrs[int(t_max/chunk_duration):]=0
+    return chunk_duration*np.argmax(corrs)
diff --git a/moviepy/compat.py b/moviepy/compat.py
index 961bc9f..e97b11b 100644
--- a/moviepy/compat.py
+++ b/moviepy/compat.py
@@ -1,11 +1,14 @@
 import os
 import sys
-PY3 = sys.version_info.major >= 3
+
+PY3=sys.version_info.major >= 3
+
 try:
-    string_types = str, unicode
+    string_types = (str, unicode)     # Python 2
 except NameError:
-    string_types = str
+    string_types = (str)              # Python 3
+   
 try:
-    from subprocess import DEVNULL
+    from subprocess import DEVNULL    # Python 3
 except ImportError:
-    DEVNULL = open(os.devnull, 'wb')
+    DEVNULL = open(os.devnull, 'wb')  # Python 2
diff --git a/moviepy/config.py b/moviepy/config.py
index 09fa2fe..b1ce2f8 100644
--- a/moviepy/config.py
+++ b/moviepy/config.py
@@ -1,16 +1,42 @@
 import os
 import subprocess as sp
+
 from .compat import DEVNULL
 from .config_defaults import FFMPEG_BINARY, IMAGEMAGICK_BINARY
+
 if os.name == 'nt':
     try:
-        import winreg as wr
+        import winreg as wr # py3k
     except ImportError:
-        import _winreg as wr
-if FFMPEG_BINARY == 'ffmpeg-imageio':
+        import _winreg as wr # py2k
+
+
+def try_cmd(cmd):
+    try:
+        popen_params = {
+            "stdout": sp.PIPE,
+            "stderr": sp.PIPE,
+            "stdin": DEVNULL
+        }
+
+        # This was added so that no extra unwanted window opens on windows
+        # when the child process is created
+        if os.name == "nt":
+            popen_params["creationflags"] = 0x08000000
+
+        proc = sp.Popen(cmd, **popen_params)
+        proc.communicate()
+    except Exception as err:
+        return False, err
+    else:
+        return True, None
+
+if FFMPEG_BINARY=='ffmpeg-imageio':
     from imageio.plugins.ffmpeg import get_exe
     FFMPEG_BINARY = get_exe()
-elif FFMPEG_BINARY == 'auto-detect':
+
+elif FFMPEG_BINARY=='auto-detect':
+
     if try_cmd(['ffmpeg'])[0]:
         FFMPEG_BINARY = 'ffmpeg'
     elif try_cmd(['ffmpeg.exe'])[0]:
@@ -20,15 +46,15 @@ elif FFMPEG_BINARY == 'auto-detect':
 else:
     success, err = try_cmd([FFMPEG_BINARY])
     if not success:
-        raise IOError(str(err) +
-            ' - The path specified for the ffmpeg binary might be wrong')
-if IMAGEMAGICK_BINARY == 'auto-detect':
+        raise IOError(
+            str(err) +
+            " - The path specified for the ffmpeg binary might be wrong")
+
+if IMAGEMAGICK_BINARY=='auto-detect':
     if os.name == 'nt':
         try:
-            key = wr.OpenKey(wr.HKEY_LOCAL_MACHINE,
-                'SOFTWARE\\ImageMagick\\Current')
-            IMAGEMAGICK_BINARY = wr.QueryValueEx(key, 'BinPath')[0
-                ] + '\\convert.exe'
+            key = wr.OpenKey(wr.HKEY_LOCAL_MACHINE, 'SOFTWARE\\ImageMagick\\Current')
+            IMAGEMAGICK_BINARY = wr.QueryValueEx(key, 'BinPath')[0] + r"\convert.exe"
             key.Close()
         except:
             IMAGEMAGICK_BINARY = 'unset'
@@ -38,34 +64,55 @@ if IMAGEMAGICK_BINARY == 'auto-detect':
         IMAGEMAGICK_BINARY = 'unset'
 else:
     if not os.path.exists(IMAGEMAGICK_BINARY):
-        raise IOError('ImageMagick binary cannot be found at {}'.format(
-            IMAGEMAGICK_BINARY))
+        raise IOError(
+            "ImageMagick binary cannot be found at {}".format(
+                IMAGEMAGICK_BINARY
+            )
+        )
+
     if not os.path.isfile(IMAGEMAGICK_BINARY):
-        raise IOError('ImageMagick binary found at {} is not a file'.format
-            (IMAGEMAGICK_BINARY))
+        raise IOError(
+            "ImageMagick binary found at {} is not a file".format(
+                IMAGEMAGICK_BINARY
+            )
+        )
+
     success, err = try_cmd([IMAGEMAGICK_BINARY])
     if not success:
-        raise IOError(
-            '%s - The path specified for the ImageMagick binary might be wrong: %s'
-             % (err, IMAGEMAGICK_BINARY))
+        raise IOError("%s - The path specified for the ImageMagick binary might "
+                      "be wrong: %s" % (err, IMAGEMAGICK_BINARY))


 def get_setting(varname):
     """ Returns the value of a configuration variable. """
-    pass
+    gl = globals()
+    if varname not in gl.keys():
+        raise ValueError("Unknown setting %s"%varname)
+    # Here, possibly add some code to raise exceptions if some
+    # parameter isn't set set properly, explaining on how to set it.
+    return gl[varname]


 def change_settings(new_settings=None, filename=None):
     """ Changes the value of configuration variables."""
-    pass
+    new_settings = new_settings or {}
+    gl = globals()
+    if filename:
+        with open(filename) as in_file:
+            exec(in_file)
+        gl.update(locals())
+    gl.update(new_settings)
+    # Here you can add some code  to check that the new configuration
+    # values are valid.


-if __name__ == '__main__':
+if __name__ == "__main__":
     if try_cmd([FFMPEG_BINARY])[0]:
-        print('MoviePy : ffmpeg successfully found.')
+        print( "MoviePy : ffmpeg successfully found." )
     else:
-        print("MoviePy : can't find or access ffmpeg.")
+        print( "MoviePy : can't find or access ffmpeg." )
+
     if try_cmd([IMAGEMAGICK_BINARY])[0]:
-        print('MoviePy : ImageMagick successfully found.')
+        print( "MoviePy : ImageMagick successfully found." )
     else:
-        print("MoviePy : can't find or access ImageMagick.")
+        print( "MoviePy : can't find or access ImageMagick." )
diff --git a/moviepy/config_defaults.py b/moviepy/config_defaults.py
index 86fcdea..d88e1f7 100644
--- a/moviepy/config_defaults.py
+++ b/moviepy/config_defaults.py
@@ -18,7 +18,7 @@ Linux/Mac:
    export FFMPEG_BINARY=path/to/ffmpeg

 Windows:
-   set FFMPEG_BINARY=path  ofmpeg
+   set FFMPEG_BINARY=path\to\ffmpeg

 Instructions
 --------------
@@ -34,7 +34,7 @@ FFMPEG_BINARY
     enter it like that:

     FFMPEG_BINARY = r"path/to/ffmpeg" # on linux
-    FFMPEG_BINARY = r"path ofmpeg.exe" # on windows
+    FFMPEG_BINARY = r"path\to\ffmpeg.exe" # on windows

     Warning: the 'r' before the path is important, especially on Windows.

@@ -44,9 +44,11 @@ IMAGEMAGICK_BINARY
     For Windows users, you must specify the path to the ImageMagick
     'magick' binary. For instance:

-    IMAGEMAGICK_BINARY = r"C:\\Program Files\\ImageMagick-6.8.8-Q16\\magick.exe"
+    IMAGEMAGICK_BINARY = r"C:\Program Files\ImageMagick-6.8.8-Q16\magick.exe"

 """
+
 import os
+
 FFMPEG_BINARY = os.getenv('FFMPEG_BINARY', 'ffmpeg-imageio')
 IMAGEMAGICK_BINARY = os.getenv('IMAGEMAGICK_BINARY', 'auto-detect')
diff --git a/moviepy/decorators.py b/moviepy/decorators.py
index 8004202..277276c 100644
--- a/moviepy/decorators.py
+++ b/moviepy/decorators.py
@@ -1,40 +1,58 @@
 """
 all decorators used in moviepy go there
 """
+
 import decorator
+
 from moviepy.tools import cvsecs


 @decorator.decorator
 def outplace(f, clip, *a, **k):
     """ Applies f(clip.copy(), *a, **k) and returns clip.copy()"""
-    pass
-
+    newclip = clip.copy()
+    f(newclip, *a, **k)
+    return newclip

 @decorator.decorator
 def convert_masks_to_RGB(f, clip, *a, **k):
     """ If the clip is a mask, convert it to RGB before running the function """
-    pass
-
+    if clip.ismask:
+        clip = clip.to_RGB()
+    return f(clip, *a, **k)

 @decorator.decorator
 def apply_to_mask(f, clip, *a, **k):
     """ This decorator will apply the same function f to the mask of
         the clip created with f """
-    pass
+        
+    newclip = f(clip, *a, **k)
+    if getattr(newclip, 'mask', None):
+        newclip.mask = f(newclip.mask, *a, **k)
+    return newclip
+


 @decorator.decorator
 def apply_to_audio(f, clip, *a, **k):
     """ This decorator will apply the function f to the audio of
         the clip created with f """
-    pass
+        
+    newclip = f(clip, *a, **k)
+    if getattr(newclip, 'audio', None):
+        newclip.audio = f(newclip.audio, *a, **k)
+    return newclip


 @decorator.decorator
 def requires_duration(f, clip, *a, **k):
     """ Raise an error if the clip has no duration."""
-    pass
+    
+    if clip.duration is None:
+        raise ValueError("Attribute 'duration' not set")
+    else:
+        return f(clip, *a, **k)
+


 @decorator.decorator
@@ -45,26 +63,73 @@ def audio_video_fx(f, clip, *a, **k):
     can be also used on a video clip, at which case it returns a
     videoclip with unmodified video and modified audio.
     """
-    pass
-
-
-def preprocess_args(fun, varnames):
+    
+    if hasattr(clip, "audio"):
+        newclip = clip.copy()
+        if clip.audio is not None:
+            newclip.audio =  f(clip.audio, *a, **k)
+        return newclip
+    else:
+        return f(clip, *a, **k)
+
+def preprocess_args(fun,varnames):
     """ Applies fun to variables in varnames before launching the function """
-    pass
+    
+    def wrapper(f, *a, **kw):
+        if hasattr(f, "func_code"):
+            func_code = f.func_code # Python 2
+        else:
+            func_code = f.__code__ # Python 3
+            
+        names = func_code.co_varnames
+        new_a = [fun(arg) if (name in varnames) else arg
+                 for (arg, name) in zip(a, names)]
+        new_kw = {k: fun(v) if k in varnames else v
+                 for (k,v) in kw.items()}
+        return f(*new_a, **new_kw)
+    return decorator.decorator(wrapper)


 def convert_to_seconds(varnames):
-    """Converts the specified variables to seconds"""
-    pass
+    "Converts the specified variables to seconds"
+    return preprocess_args(cvsecs, varnames)
+


 @decorator.decorator
 def add_mask_if_none(f, clip, *a, **k):
-    """ Add a mask to the clip if there is none. """
-    pass
+    """ Add a mask to the clip if there is none. """        
+    if clip.mask is None:
+        clip = clip.add_mask()
+    return f(clip, *a, **k)
+


 @decorator.decorator
 def use_clip_fps_by_default(f, clip, *a, **k):
     """ Will use clip.fps if no fps=... is provided in **k """
-    pass
+    
+    def fun(fps):
+        if fps is not None:
+            return fps
+        elif getattr(clip, 'fps', None):
+            return clip.fps
+        raise AttributeError("No 'fps' (frames per second) attribute specified"
+                " for function %s and the clip has no 'fps' attribute. Either"
+                " provide e.g. fps=24 in the arguments of the function, or define"
+                " the clip's fps with `clip.fps=24`" % f.__name__)
+
+
+    if hasattr(f, "func_code"):
+        func_code = f.func_code # Python 2
+    else:
+        func_code = f.__code__ # Python 3
+        
+    names = func_code.co_varnames[1:]
+    
+    new_a = [fun(arg) if (name=='fps') else arg
+             for (arg, name) in zip(a, names)]
+    new_kw = {k: fun(v) if k=='fps' else v
+             for (k,v) in k.items()}
+
+    return f(clip, *new_a, **new_kw)
diff --git a/moviepy/editor.py b/moviepy/editor.py
index 59873ec..c5dff72 100644
--- a/moviepy/editor.py
+++ b/moviepy/editor.py
@@ -13,61 +13,119 @@ we can write
 It also starts a PyGame session (if PyGame is installed) and enables
 clip.preview().
 """
+
+# Note that these imports could have been performed in the __init__.py
+# file, but this would make the loading of moviepy slower.
+
 import os
 import sys
+
+# Downloads ffmpeg if it isn't already installed
 import imageio
+# Checks to see if the user has set a place for their own version of ffmpeg
+
 if os.getenv('FFMPEG_BINARY') is None:
     if sys.version_info < (3, 4):
+        #uses an old version of imageio with ffmpeg.download.
         imageio.plugins.ffmpeg.download()
-os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
+
+# Hide the welcome message from pygame: https://github.com/pygame/pygame/issues/542
+os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "1"
+
+# Clips
 from .video.io.VideoFileClip import VideoFileClip
 from .video.io.ImageSequenceClip import ImageSequenceClip
 from .video.io.downloader import download_webfile
 from .video.VideoClip import VideoClip, ImageClip, ColorClip, TextClip
 from .video.compositing.CompositeVideoClip import CompositeVideoClip, clips_array
-from .video.compositing.concatenate import concatenate_videoclips, concatenate
+from .video.compositing.concatenate import concatenate_videoclips, concatenate # concatenate=deprecated
+
 from .audio.AudioClip import AudioClip, CompositeAudioClip, concatenate_audioclips
 from .audio.io.AudioFileClip import AudioFileClip
+
+# FX
+
 import moviepy.video.fx.all as vfx
 import moviepy.audio.fx.all as afx
 import moviepy.video.compositing.transitions as transfx
+
+# Tools
+
 import moviepy.video.tools as videotools
 import moviepy.video.io.ffmpeg_tools as ffmpeg_tools
 from .video.io.html_tools import ipython_display
 from .tools import cvsecs
+
 try:
     from .video.io.sliders import sliders
 except ImportError:
     pass
-for method in ['afx.audio_fadein', 'afx.audio_fadeout',
-    'afx.audio_normalize', 'afx.volumex', 'transfx.crossfadein',
-    'transfx.crossfadeout', 'vfx.crop', 'vfx.fadein', 'vfx.fadeout',
-    'vfx.invert_colors', 'vfx.loop', 'vfx.margin', 'vfx.mask_and',
-    'vfx.mask_or', 'vfx.resize', 'vfx.rotate', 'vfx.speedx']:
-    exec('VideoClip.%s = %s' % (method.split('.')[1], method))
-for method in ['afx.audio_fadein', 'afx.audio_fadeout', 'afx.audio_loop',
-    'afx.audio_normalize', 'afx.volumex']:
-    exec('AudioClip.%s = %s' % (method.split('.')[1], method))
+
+# The next loop transforms many effects into VideoClip methods so that
+# they can be walled with myclip.resize(width=500) instead of 
+# myclip.fx( vfx.resize, width= 500)
+for method in [
+          "afx.audio_fadein",
+          "afx.audio_fadeout",
+          "afx.audio_normalize",
+          "afx.volumex",
+          "transfx.crossfadein",
+          "transfx.crossfadeout",
+          "vfx.crop",
+          "vfx.fadein",
+          "vfx.fadeout",
+          "vfx.invert_colors",
+          "vfx.loop",
+          "vfx.margin",
+          "vfx.mask_and",
+          "vfx.mask_or",
+          "vfx.resize",
+          "vfx.rotate",
+          "vfx.speedx"
+          ]:
+
+    exec("VideoClip.%s = %s" % (method.split('.')[1], method))
+
+
+for method in ["afx.audio_fadein",
+               "afx.audio_fadeout",
+               "afx.audio_loop",
+               "afx.audio_normalize",
+               "afx.volumex"
+              ]:
+              
+    exec("AudioClip.%s = %s" % (method.split('.')[1], method))
+
+
+# adds easy ipython integration
 VideoClip.ipython_display = ipython_display
 AudioClip.ipython_display = ipython_display
+#-----------------------------------------------------------------
+# Previews: try to import pygame, else make methods which raise
+# exceptions saying to install PyGame
+
+
+# Add methods preview and show (only if pygame installed)
 try:
     from moviepy.video.io.preview import show, preview
 except ImportError:
-
     def preview(self, *args, **kwargs):
         """NOT AVAILABLE : clip.preview requires Pygame installed."""
-        pass
+        raise ImportError("clip.preview requires Pygame installed")

     def show(self, *args, **kwargs):
         """NOT AVAILABLE : clip.show requires Pygame installed."""
-        pass
+        raise ImportError("clip.show requires Pygame installed")
+
+
 VideoClip.preview = preview
 VideoClip.show = show
+
 try:
     from moviepy.audio.io.preview import preview
 except ImportError:
-
     def preview(self, *args, **kwargs):
         """ NOT AVAILABLE : clip.preview requires Pygame installed."""
-        pass
+        raise ImportError("clip.preview requires Pygame installed")
+
 AudioClip.preview = preview
diff --git a/moviepy/tools.py b/moviepy/tools.py
index 51edf95..5357c3a 100644
--- a/moviepy/tools.py
+++ b/moviepy/tools.py
@@ -5,18 +5,27 @@ import os
 import subprocess as sp
 import sys
 import warnings
+
 import proglog
+
 from .compat import DEVNULL


+
 def sys_write_flush(s):
     """ Writes and flushes without delay a text in the console """
-    pass
+    # Reason for not using `print` is that in some consoles "print" 
+    # commands get delayed, while stdout.flush are instantaneous, 
+    # so this method is better at providing feedback.
+    # See https://github.com/Zulko/moviepy/pull/485
+    sys.stdout.write(s)
+    sys.stdout.flush()


 def verbose_print(verbose, s):
     """ Only prints s (with sys_write_flush) if verbose is True."""
-    pass
+    if verbose:
+        sys_write_flush(s)


 def subprocess_call(cmd, logger='bar', errorprint=True):
@@ -24,13 +33,37 @@ def subprocess_call(cmd, logger='bar', errorprint=True):

     Set logger to None or a custom Proglog logger to avoid printings.
     """
-    pass
+    logger = proglog.default_bar_logger(logger)
+    logger(message='Moviepy - Running:\n>>> "+ " ".join(cmd)')
+
+    popen_params = {"stdout": DEVNULL,
+                    "stderr": sp.PIPE,
+                    "stdin": DEVNULL}
+
+    if os.name == "nt":
+        popen_params["creationflags"] = 0x08000000
+
+    proc = sp.Popen(cmd, **popen_params)

+    out, err = proc.communicate() # proc.wait()
+    proc.stderr.close()
+
+    if proc.returncode:
+        if errorprint:
+            logger(message='Moviepy - Command returned an error')
+        raise IOError(err.decode('utf8'))
+    else:
+        logger(message='Moviepy - Command successful')
+
+    del proc

 def is_string(obj):
     """ Returns true if s is string or string-like object,
     compatible with Python 2 and Python 3."""
-    pass
+    try:
+        return isinstance(obj, basestring)
+    except NameError:
+        return isinstance(obj, str)


 def cvsecs(time):
@@ -56,7 +89,15 @@ def cvsecs(time):
     >>> cvsecs('33.5')      # only secs
     33.5
     """
-    pass
+    factors = (1, 60, 3600)
+    
+    if is_string(time):     
+        time = [float(f.replace(',', '.')) for f in time.split(':')]
+
+    if not isinstance(time, (tuple, list)):
+        return time
+
+    return sum(mult * part for mult, part in zip(factors, reversed(time)))


 def deprecated_version_of(f, oldname, newname=None):
@@ -84,15 +125,53 @@ def deprecated_version_of(f, oldname, newname=None):
     >>>
     >>> Clip.to_file = deprecated_version_of(Clip.write_file, 'to_file')
     """
-    pass
-
-
-extensions_dict = {'mp4': {'type': 'video', 'codec': ['libx264', 'libmpeg4',
-    'aac']}, 'ogv': {'type': 'video', 'codec': ['libtheora']}, 'webm': {
-    'type': 'video', 'codec': ['libvpx']}, 'avi': {'type': 'video'}, 'mov':
-    {'type': 'video'}, 'ogg': {'type': 'audio', 'codec': ['libvorbis']},
-    'mp3': {'type': 'audio', 'codec': ['libmp3lame']}, 'wav': {'type':
-    'audio', 'codec': ['pcm_s16le', 'pcm_s24le', 'pcm_s32le']}, 'm4a': {
-    'type': 'audio', 'codec': ['libfdk_aac']}}
-for ext in ['jpg', 'jpeg', 'png', 'bmp', 'tiff']:
-    extensions_dict[ext] = {'type': 'image'}
+
+    if newname is None: newname = f.__name__
+
+    warning= ("The function ``%s`` is deprecated and is kept temporarily "
+              "for backwards compatibility.\nPlease use the new name, "
+              "``%s``, instead.")%(oldname, newname)
+
+    def fdepr(*a, **kw):
+        warnings.warn("MoviePy: " + warning, PendingDeprecationWarning)
+        return f(*a, **kw)
+    fdepr.__doc__ = warning
+
+    return fdepr
+
+
+# non-exhaustive dictionnary to store default informations.
+# any addition is most welcome.
+# Note that 'gif' is complicated to place. From a VideoFileClip point of view,
+# it is a video, but from a HTML5 point of view, it is an image.
+
+extensions_dict = { "mp4":  {'type':'video', 'codec':['libx264','libmpeg4', 'aac']},
+                    'ogv':  {'type':'video', 'codec':['libtheora']},
+                    'webm': {'type':'video', 'codec':['libvpx']},
+                    'avi':  {'type':'video'},
+                    'mov':  {'type':'video'},
+
+                    'ogg':  {'type':'audio', 'codec':['libvorbis']},
+                    'mp3':  {'type':'audio', 'codec':['libmp3lame']},
+                    'wav':  {'type':'audio', 'codec':['pcm_s16le', 'pcm_s24le', 'pcm_s32le']},
+                    'm4a':  {'type':'audio', 'codec':['libfdk_aac']}
+                  }
+
+for ext in ["jpg", "jpeg", "png", "bmp", "tiff"]:
+    extensions_dict[ext] = {'type':'image'}
+
+
+def find_extension(codec):
+    if codec in extensions_dict:
+        # codec is already the extension
+        return codec
+
+    for ext,infos in extensions_dict.items():
+        if codec in infos.get('codec', []):
+            return ext
+    raise ValueError(
+        "The audio_codec you chose is unknown by MoviePy. "
+        "You should report this. In the meantime, you can "
+        "specify a temp_audiofile with the right extension "
+        "in write_videofile."
+    )
diff --git a/moviepy/utils.py b/moviepy/utils.py
index dfc7f21..705cb49 100644
--- a/moviepy/utils.py
+++ b/moviepy/utils.py
@@ -1,5 +1,20 @@
 from moviepy.audio.io.AudioFileClip import AudioFileClip
 from moviepy.video.io.VideoFileClip import VideoFileClip
 from moviepy.video.VideoClip import ImageClip
-CLIP_TYPES = {'audio': AudioFileClip, 'video': VideoFileClip, 'image':
-    ImageClip}
+
+
+CLIP_TYPES = {
+    'audio': AudioFileClip,
+    'video': VideoFileClip,
+    'image': ImageClip,
+}
+
+def close_all_clips(objects='globals', types=('audio', 'video', 'image')):
+    if objects == 'globals':
+        objects = globals()
+    if hasattr(objects, 'values'):
+        objects = objects.values()
+    types_tuple = tuple(CLIP_TYPES[key] for key in types)
+    for obj in objects:
+        if isinstance(obj, types_tuple):
+            obj.close()
diff --git a/moviepy/version.py b/moviepy/version.py
index 3f6fab6..976498a 100644
--- a/moviepy/version.py
+++ b/moviepy/version.py
@@ -1 +1 @@
-__version__ = '1.0.3'
+__version__ = "1.0.3"
diff --git a/moviepy/video/VideoClip.py b/moviepy/video/VideoClip.py
index d855ddb..d19d253 100644
--- a/moviepy/video/VideoClip.py
+++ b/moviepy/video/VideoClip.py
@@ -8,16 +8,22 @@ import os
 import subprocess as sp
 import tempfile
 import warnings
+
 import numpy as np
 import proglog
 from imageio import imread, imsave
+
 from ..Clip import Clip
 from ..compat import DEVNULL, string_types
 from ..config import get_setting
-from ..decorators import add_mask_if_none, apply_to_mask, convert_masks_to_RGB, convert_to_seconds, outplace, requires_duration, use_clip_fps_by_default
-from ..tools import deprecated_version_of, extensions_dict, find_extension, is_string, subprocess_call
+from ..decorators import (add_mask_if_none, apply_to_mask,
+                          convert_masks_to_RGB, convert_to_seconds, outplace,
+                          requires_duration, use_clip_fps_by_default)
+from ..tools import (deprecated_version_of, extensions_dict, find_extension,
+                     is_string, subprocess_call)
 from .io.ffmpeg_writer import ffmpeg_write_video
-from .io.gif_writers import write_gif, write_gif_with_image_io, write_gif_with_tempfiles
+from .io.gif_writers import (write_gif, write_gif_with_image_io,
+                             write_gif_with_tempfiles)
 from .tools.drawing import blit


@@ -69,7 +75,7 @@ class VideoClip(Clip):
     """

     def __init__(self, make_frame=None, ismask=False, duration=None,
-        has_constant_size=True):
+                 has_constant_size=True):
         Clip.__init__(self)
         self.mask = None
         self.audio = None
@@ -79,11 +85,26 @@ class VideoClip(Clip):
             self.make_frame = make_frame
             self.size = self.get_frame(0).shape[:2][::-1]
         self.ismask = ismask
-        self.has_constant_size = has_constant_size
+        self.has_constant_size=has_constant_size
         if duration is not None:
             self.duration = duration
             self.end = duration

+    @property
+    def w(self):
+        return self.size[0]
+
+    @property
+    def h(self):
+        return self.size[1]
+
+    @property
+    def aspect_ratio(self):
+        return self.w / float(self.h)
+
+    # ===============================================================
+    # EXPORT OPERATIONS
+
     @convert_to_seconds(['t'])
     @convert_masks_to_RGB
     def save_frame(self, filename, t=0, withmask=True):
@@ -97,17 +118,29 @@ class VideoClip(Clip):
         the alpha layer of the picture (only works with PNGs).

         """
-        pass
+
+        im = self.get_frame(t)
+        if withmask and self.mask is not None:
+            mask = 255 * self.mask.get_frame(t)
+            im = np.dstack([im, mask]).astype('uint8')
+        else:
+            im = im.astype("uint8")
+
+        imsave(filename, im)

     @requires_duration
     @use_clip_fps_by_default
     @convert_masks_to_RGB
-    def write_videofile(self, filename, fps=None, codec=None, bitrate=None,
-        audio=True, audio_fps=44100, preset='medium', audio_nbytes=4,
-        audio_codec=None, audio_bitrate=None, audio_bufsize=2000,
-        temp_audiofile=None, rewrite_audio=True, remove_temp=True,
-        write_logfile=False, verbose=True, threads=None, ffmpeg_params=None,
-        logger='bar'):
+    def write_videofile(self, filename, fps=None, codec=None,
+                        bitrate=None, audio=True, audio_fps=44100,
+                        preset="medium",
+                        audio_nbytes=4, audio_codec=None,
+                        audio_bitrate=None, audio_bufsize=2000,
+                        temp_audiofile=None,
+                        rewrite_audio=True, remove_temp=True,
+                        write_logfile=False, verbose=True,
+                        threads=None, ffmpeg_params=None,
+                        logger='bar'):
         """Write the clip to a videofile.

         Parameters
@@ -219,13 +252,70 @@ class VideoClip(Clip):
         >>> clip.close()

         """
-        pass
+        name, ext = os.path.splitext(os.path.basename(filename))
+        ext = ext[1:].lower()
+        logger = proglog.default_bar_logger(logger)
+
+        if codec is None:
+
+            try:
+                codec = extensions_dict[ext]['codec'][0]
+            except KeyError:
+                raise ValueError("MoviePy couldn't find the codec associated "
+                                 "with the filename. Provide the 'codec' "
+                                 "parameter in write_videofile.")
+
+        if audio_codec is None:
+            if ext in ['ogv', 'webm']:
+                audio_codec = 'libvorbis'
+            else:
+                audio_codec = 'libmp3lame'
+        elif audio_codec == 'raw16':
+            audio_codec = 'pcm_s16le'
+        elif audio_codec == 'raw32':
+            audio_codec = 'pcm_s32le'
+
+        audiofile = audio if is_string(audio) else None
+        make_audio = ((audiofile is None) and (audio == True) and
+                      (self.audio is not None))
+
+        if make_audio and temp_audiofile:
+            # The audio will be the clip's audio
+            audiofile = temp_audiofile
+        elif make_audio:
+            audio_ext = find_extension(audio_codec)
+            audiofile = (name + Clip._TEMP_FILES_PREFIX + "wvf_snd.%s" % audio_ext)
+
+        # enough cpu for multiprocessing ? USELESS RIGHT NOW, WILL COME AGAIN
+        # enough_cpu = (multiprocessing.cpu_count() > 1)
+        logger(message="Moviepy - Building video %s." % filename)
+        if make_audio:
+            self.audio.write_audiofile(audiofile, audio_fps,
+                                       audio_nbytes, audio_bufsize,
+                                       audio_codec, bitrate=audio_bitrate,
+                                       write_logfile=write_logfile,
+                                       verbose=verbose,
+                                       logger=logger)
+
+        ffmpeg_write_video(self, filename, fps, codec,
+                           bitrate=bitrate,
+                           preset=preset,
+                           write_logfile=write_logfile,
+                           audiofile=audiofile,
+                           verbose=verbose, threads=threads,
+                           ffmpeg_params=ffmpeg_params,
+                           logger=logger)
+
+        if remove_temp and make_audio:
+            if os.path.exists(audiofile):
+                os.remove(audiofile)
+        logger(message="Moviepy - video ready %s" % filename)

     @requires_duration
     @use_clip_fps_by_default
     @convert_masks_to_RGB
     def write_images_sequence(self, nameformat, fps=None, verbose=True,
-        withmask=True, logger='bar'):
+                              withmask=True, logger='bar'):
         """ Writes the videoclip to a sequence of image files.

         Parameters
@@ -265,13 +355,26 @@ class VideoClip(Clip):
         ``ImageSequenceClip``.

         """
-        pass
+        logger = proglog.default_bar_logger(logger)
+        logger(message='Moviepy - Writing frames %s.' % nameformat)
+
+        tt = np.arange(0, self.duration, 1.0 / fps)
+
+        filenames = []
+        for i, t in logger.iter_bar(t=list(enumerate(tt))):
+            name = nameformat % i
+            filenames.append(name)
+            self.save_frame(name, t, withmask=withmask)
+        logger(message='Moviepy - Done writing frames %s.' % nameformat)
+
+        return filenames

     @requires_duration
     @convert_masks_to_RGB
-    def write_gif(self, filename, fps=None, program='imageio', opt='nq',
-        fuzz=1, verbose=True, loop=0, dispose=False, colors=None, tempfiles
-        =False, logger='bar'):
+    def write_gif(self, filename, fps=None, program='imageio',
+                  opt='nq', fuzz=1, verbose=True,
+                  loop=0, dispose=False, colors=None, tempfiles=False,
+                  logger='bar'):
         """ Write the VideoClip to a GIF file.

         Converts a VideoClip into an animated GIF using ImageMagick
@@ -322,7 +425,33 @@ class VideoClip(Clip):
             >>> myClip.speedx(0.5).to_gif('myClip.gif')

         """
-        pass
+        # A little sketchy at the moment, maybe move all that in write_gif,
+        #  refactor a little... we will see.
+
+        if program == 'imageio':
+            write_gif_with_image_io(self, filename, fps=fps, opt=opt, loop=loop,
+                                    verbose=verbose, colors=colors,
+                                    logger=logger)
+        elif tempfiles:
+            # convert imageio opt variable to something that can be used with
+            # ImageMagick
+            opt = 'optimizeplus' if opt == 'nq' else 'OptimizeTransparency'
+            write_gif_with_tempfiles(self, filename, fps=fps,
+                                     program=program, opt=opt, fuzz=fuzz,
+                                     verbose=verbose, loop=loop,
+                                     dispose=dispose, colors=colors,
+                                     logger=logger)
+        else:
+            # convert imageio opt variable to something that can be used with
+            # ImageMagick
+            opt = 'optimizeplus' if opt == 'nq' else 'OptimizeTransparency'
+            write_gif(self, filename, fps=fps, program=program,
+                      opt=opt, fuzz=fuzz, verbose=verbose, loop=loop,
+                      dispose=dispose, colors=colors,
+                      logger=logger)
+
+    # -----------------------------------------------------------------
+    # F I L T E R I N G

     def subfx(self, fx, ta=0, tb=None, **kwargs):
         """Apply a transformation to a part of the clip.
@@ -339,14 +468,46 @@ class VideoClip(Clip):
         >>> newclip = clip.subapply(lambda c:c.speedx(0.5) , 3,6)

         """
-        pass
+        left = self.subclip(0, ta) if ta else None
+        center = self.subclip(ta, tb).fx(fx, **kwargs)
+        right = self.subclip(t_start=tb) if tb else None
+
+        clips = [c for c in (left, center, right) if c]
+
+        # beurk, have to find other solution
+        from moviepy.video.compositing.concatenate import concatenate_videoclips
+
+        return concatenate_videoclips(clips).set_start(self.start)
+
+    # IMAGE FILTERS

     def fl_image(self, image_func, apply_to=None):
         """
         Modifies the images of a clip by replacing the frame
         `get_frame(t)` by another frame,  `image_func(get_frame(t))`
         """
-        pass
+        apply_to = apply_to or []
+        return self.fl(lambda gf, t: image_func(gf(t)), apply_to)
+
+    # --------------------------------------------------------------
+    # C O M P O S I T I N G
+
+    def fill_array(self, pre_array, shape=(0, 0)):
+        pre_shape = pre_array.shape
+        dx = shape[0] - pre_shape[0]
+        dy = shape[1] - pre_shape[1]
+        post_array = pre_array
+        if dx < 0:
+            post_array = pre_array[:shape[0]]
+        elif dx > 0:
+            x_1 = [[[1, 1, 1]] * pre_shape[1]] * dx
+            post_array = np.vstack((pre_array, x_1))
+        if dy < 0:
+            post_array = post_array[:, :shape[1]]
+        elif dy > 0:
+            x_1 = [[[1, 1, 1]] * dy] * post_array.shape[0]
+            post_array = np.hstack((post_array, x_1))
+        return post_array

     def blit_on(self, picture, t):
         """
@@ -354,7 +515,53 @@ class VideoClip(Clip):
         on the given `picture`, the position of the clip being given
         by the clip's ``pos`` attribute. Meant for compositing.
         """
-        pass
+        hf, wf = framesize = picture.shape[:2]
+
+        if self.ismask and picture.max():
+            return np.minimum(1, picture + self.blit_on(np.zeros(framesize), t))
+
+        ct = t - self.start  # clip time
+
+        # GET IMAGE AND MASK IF ANY
+
+        img = self.get_frame(ct)
+        mask = self.mask.get_frame(ct) if self.mask else None                
+        
+        if mask is not None and ((img.shape[0] != mask.shape[0]) or (img.shape[1] != mask.shape[1])):
+            img = self.fill_array(img, mask.shape)
+
+        hi, wi = img.shape[:2]
+
+        # SET POSITION
+        pos = self.pos(ct)
+
+        # preprocess short writings of the position
+        if isinstance(pos, str):
+            pos = {'center': ['center', 'center'],
+                   'left': ['left', 'center'],
+                   'right': ['right', 'center'],
+                   'top': ['center', 'top'],
+                   'bottom': ['center', 'bottom']}[pos]
+        else:
+            pos = list(pos)
+
+        # is the position relative (given in % of the clip's size) ?
+        if self.relative_pos:
+            for i, dim in enumerate([wf, hf]):
+                if not isinstance(pos[i], str):
+                    pos[i] = dim * pos[i]
+
+        if isinstance(pos[0], str):
+            D = {'left': 0, 'center': (wf - wi) / 2, 'right': wf - wi}
+            pos[0] = D[pos[0]]
+
+        if isinstance(pos[1], str):
+            D = {'top': 0, 'center': (hf - hi) / 2, 'bottom': hf - hi}
+            pos[1] = D[pos[1]]
+
+        pos = map(int, pos)
+
+        return blit(img, picture, pos, mask=mask, ismask=self.ismask)

     def add_mask(self):
         """Add a mask VideoClip to the VideoClip.
@@ -366,9 +573,16 @@ class VideoClip(Clip):
         Set ``constant_size`` to  `False` for clips with moving
         image size.
         """
-        pass
+        if self.has_constant_size:
+            mask = ColorClip(self.size, 1.0, ismask=True)
+            return self.set_mask(mask.set_duration(self.duration))
+        else:
+            make_frame = lambda t: np.ones(self.get_frame(t).shape[:2], dtype=float)
+            mask = VideoClip(ismask=True, make_frame=make_frame)
+            return self.set_mask(mask.set_duration(self.duration))

-    def on_color(self, size=None, color=(0, 0, 0), pos=None, col_opacity=None):
+    def on_color(self, size=None, color=(0, 0, 0), pos=None,
+                 col_opacity=None):
         """Place the clip on a colored background.

         Returns a clip made of the current clip overlaid on a color
@@ -393,7 +607,31 @@ class VideoClip(Clip):
           background.

         """
-        pass
+        from .compositing.CompositeVideoClip import CompositeVideoClip
+
+        if size is None:
+            size = self.size
+        if pos is None:
+            pos = 'center'
+        colorclip = ColorClip(size, color=color)
+
+        if col_opacity is not None:
+            colorclip = (ColorClip(size, color=color, duration=self.duration)
+                         .set_opacity(col_opacity))
+            result = CompositeVideoClip([colorclip, self.set_position(pos)])
+        else:
+            result = CompositeVideoClip([self.set_position(pos)],
+                                        size=size,
+                                        bg_color=color)
+
+        if (isinstance(self, ImageClip) and (not hasattr(pos, "__call__"))
+                and ((self.mask is None) or isinstance(self.mask, ImageClip))):
+            new_result = result.to_ImageClip()
+            if result.mask is not None:
+                new_result.mask = result.mask.to_ImageClip()
+            return new_result.set_duration(result.duration)
+
+        return result

     @outplace
     def set_make_frame(self, mf):
@@ -402,7 +640,8 @@ class VideoClip(Clip):
         Returns a copy of the VideoClip instance, with the make_frame
         attribute set to `mf`.
         """
-        pass
+        self.make_frame = mf
+        self.size = self.get_frame(0).shape[:2][::-1]

     @outplace
     def set_audio(self, audioclip):
@@ -411,7 +650,7 @@ class VideoClip(Clip):
         Returns a copy of the VideoClip instance, with the `audio`
         attribute set to ``audio``, which must be an AudioClip instance.
         """
-        pass
+        self.audio = audioclip

     @outplace
     def set_mask(self, mask):
@@ -419,7 +658,8 @@ class VideoClip(Clip):

         Returns a copy of the VideoClip with the mask attribute set to
         ``mask``, which must be a greyscale (values in 0-1) VideoClip"""
-        pass
+        assert mask is None or mask.ismask
+        self.mask = mask

     @add_mask_if_none
     @outplace
@@ -429,7 +669,7 @@ class VideoClip(Clip):
         Returns a semi-transparent copy of the clip where the mask is
         multiplied by ``op`` (any float, normally between 0 and 1).
         """
-        pass
+        self.mask = self.mask.fl_image(lambda pic: op * pic)

     @apply_to_mask
     @outplace
@@ -457,7 +697,14 @@ class VideoClip(Clip):
         >>> clip.set_position(lambda t: ('center', 50+t) )

         """
-        pass
+        self.relative_pos = relative
+        if hasattr(pos, '__call__'):
+            self.pos = pos
+        else:
+            self.pos = lambda t: pos
+
+    # --------------------------------------------------------------
+    # CONVERSIONS TO OTHER TYPES

     @convert_to_seconds(['t'])
     def to_ImageClip(self, t=0, with_mask=True, duration=None):
@@ -466,15 +713,34 @@ class VideoClip(Clip):
         which can be expressed in seconds (15.35), in (min, sec),
         in (hour, min, sec), or as a string: '01:03:05.35'.
         """
-        pass
+        newclip = ImageClip(self.get_frame(t), ismask=self.ismask,
+                            duration=duration)
+        if with_mask and self.mask is not None:
+            newclip.mask = self.mask.to_ImageClip(t)
+        return newclip

     def to_mask(self, canal=0):
         """Return a mask a video clip made from the clip."""
-        pass
+        if self.ismask:
+            return self
+        else:
+            newclip = self.fl_image(lambda pic:
+                                    1.0 * pic[:, :, canal] / 255)
+            newclip.ismask = True
+            return newclip

     def to_RGB(self):
         """Return a non-mask video clip made from the mask video clip."""
-        pass
+        if self.ismask:
+            f = lambda pic: np.dstack(3 * [255 * pic]).astype('uint8')
+            newclip = self.fl_image(f)
+            newclip.ismask = False
+            return newclip
+        else:
+            return self
+
+    # ----------------------------------------------------------------
+    # Audio

     @outplace
     def without_audio(self):
@@ -483,7 +749,7 @@ class VideoClip(Clip):
         Return a copy of the clip with audio set to None.

         """
-        pass
+        self.audio = None

     @outplace
     def afx(self, fun, *a, **k):
@@ -492,7 +758,7 @@ class VideoClip(Clip):
         Return a new clip whose audio has been transformed by ``fun``.

         """
-        pass
+        self.audio = self.audio.fx(fun, *a, **k)


 class DataVideoClip(VideoClip):
@@ -516,13 +782,14 @@ class DataVideoClip(VideoClip):
     """

     def __init__(self, data, data_to_frame, fps, ismask=False,
-        has_constant_size=True):
+                 has_constant_size=True):
         self.data = data
         self.data_to_frame = data_to_frame
         self.fps = fps
-        make_frame = lambda t: self.data_to_frame(self.data[int(self.fps * t)])
-        VideoClip.__init__(self, make_frame, ismask=ismask, duration=1.0 *
-            len(data) / fps, has_constant_size=has_constant_size)
+        make_frame = lambda t: self.data_to_frame(self.data[int(self.fps*t)])
+        VideoClip.__init__(self, make_frame, ismask=ismask,
+                           duration=1.0*len(data)/fps,
+                           has_constant_size=has_constant_size)


 class UpdatedVideoClip(VideoClip):
@@ -565,8 +832,9 @@ class UpdatedVideoClip(VideoClip):
             while self.world.clip_t < t:
                 world.update()
             return world.to_frame()
-        VideoClip.__init__(self, make_frame=make_frame, ismask=ismask,
-            duration=duration)
+
+        VideoClip.__init__(self, make_frame=make_frame,
+                           ismask=ismask, duration=duration)


 """---------------------------------------------------------------------
@@ -613,23 +881,29 @@ class ImageClip(VideoClip):

     """

-    def __init__(self, img, ismask=False, transparent=True, fromalpha=False,
-        duration=None):
+    def __init__(self, img, ismask=False, transparent=True,
+                 fromalpha=False, duration=None):
         VideoClip.__init__(self, ismask=ismask, duration=duration)
+
         if isinstance(img, string_types):
             img = imread(img)
-        if len(img.shape) == 3:
+
+        if len(img.shape) == 3:  # img is (now) a RGB(a) numpy array
+
             if img.shape[2] == 4:
                 if fromalpha:
                     img = 1.0 * img[:, :, 3] / 255
                 elif ismask:
                     img = 1.0 * img[:, :, 0] / 255
                 elif transparent:
-                    self.mask = ImageClip(1.0 * img[:, :, 3] / 255, ismask=True
-                        )
+                    self.mask = ImageClip(
+                        1.0 * img[:, :, 3] / 255, ismask=True)
                     img = img[:, :, :3]
             elif ismask:
                 img = 1.0 * img[:, :, 0] / 255
+
+        # if the image was just a 2D mask, it should arrive here
+        # unchanged
         self.make_frame = lambda t: img
         self.size = img.shape[:2][::-1]
         self.img = img
@@ -640,7 +914,14 @@ class ImageClip(VideoClip):
         Equivalent to VideoClip.fl . The result is no more an
         ImageClip, it has the class VideoClip (since it may be animated)
         """
-        pass
+        if apply_to is None:
+            apply_to = []
+        # When we use fl on an image clip it may become animated.
+        # Therefore the result is not an ImageClip, just a VideoClip.
+        newclip = VideoClip.fl(self, fl, apply_to=apply_to,
+                               keep_duration=keep_duration)
+        newclip.__class__ = VideoClip
+        return newclip

     @outplace
     def fl_image(self, image_func, apply_to=None):
@@ -650,10 +931,22 @@ class ImageClip(VideoClip):
         tranformed clip is computed once and for all at the beginning,
         and not for each 'frame'.
         """
-        pass
+        if apply_to is None:
+                apply_to = []
+        arr = image_func(self.get_frame(0))
+        self.size = arr.shape[:2][::-1]
+        self.make_frame = lambda t: arr
+        self.img = arr
+
+        for attr in apply_to:
+            a = getattr(self, attr, None)
+            if a is not None:
+                new_a = a.fl_image(image_func)
+                setattr(self, attr, new_a)

     @outplace
-    def fl_time(self, time_func, apply_to=None, keep_duration=False):
+    def fl_time(self, time_func, apply_to=None,
+                keep_duration=False):
         """Time-transformation filter.

         Applies a transformation to the clip's timeline
@@ -662,15 +955,27 @@ class ImageClip(VideoClip):
         This method does nothing for ImageClips (but it may affect their
         masks or their audios). The result is still an ImageClip.
         """
-        pass
-
-
-VideoClip.set_pos = deprecated_version_of(VideoClip.set_position, 'set_pos')
+        if apply_to is None:
+            apply_to = ['mask', 'audio']
+        for attr in apply_to:
+            a = getattr(self, attr, None)
+            if a is not None:
+                new_a = a.fl_time(time_func)
+                setattr(self, attr, new_a)
+
+
+# ##
+#
+# The old functions to_videofile, to_gif, to_images sequences have been
+# replaced by the more explicite write_videofile, write_gif, etc.
+
+VideoClip.set_pos = deprecated_version_of(VideoClip.set_position,
+                                          'set_pos')
 VideoClip.to_videofile = deprecated_version_of(VideoClip.write_videofile,
-    'to_videofile')
+                                               'to_videofile')
 VideoClip.to_gif = deprecated_version_of(VideoClip.write_gif, 'to_gif')
-VideoClip.to_images_sequence = deprecated_version_of(VideoClip.
-    write_images_sequence, 'to_images_sequence')
+VideoClip.to_images_sequence = deprecated_version_of(VideoClip.write_images_sequence,
+                                                     'to_images_sequence')


 class ColorClip(ImageClip):
@@ -694,22 +999,20 @@ class ColorClip(ImageClip):
       Has been deprecated. Do not use.
     """

-    def __init__(self, size, color=None, ismask=False, duration=None, col=None
-        ):
+    def __init__(self, size, color=None, ismask=False, duration=None, col=None):
         if col is not None:
-            warnings.warn(
-                'The `ColorClip` parameter `col` has been deprecated. Please use `color` instead.'
-                , DeprecationWarning)
+            warnings.warn("The `ColorClip` parameter `col` has been deprecated."
+                          " Please use `color` instead.", DeprecationWarning)
             if color is not None:
-                warnings.warn(
-                    'The arguments `color` and `col` have both been passed to `ColorClip` so `col` has been ignored.'
-                    , UserWarning)
+                warnings.warn("The arguments `color` and `col` have both been "
+                              "passed to `ColorClip` so `col` has been ignored.",
+                              UserWarning)
             else:
                 color = col
         w, h = size
         shape = (h, w) if np.isscalar(color) else (h, w, len(color))
         ImageClip.__init__(self, np.tile(color, w * h).reshape(shape),
-            ismask=ismask, duration=duration)
+                           ismask=ismask, duration=duration)


 class TextClip(ImageClip):
@@ -777,63 +1080,76 @@ class TextClip(ImageClip):
     """

     def __init__(self, txt=None, filename=None, size=None, color='black',
-        bg_color='transparent', fontsize=None, font='Courier', stroke_color
-        =None, stroke_width=1, method='label', kerning=None, align='center',
-        interline=None, tempfilename=None, temptxt=None, transparent=True,
-        remove_temp=True, print_cmd=False):
+                 bg_color='transparent', fontsize=None, font='Courier',
+                 stroke_color=None, stroke_width=1, method='label',
+                 kerning=None, align='center', interline=None,
+                 tempfilename=None, temptxt=None,
+                 transparent=True, remove_temp=True,
+                 print_cmd=False):
+
         if txt is not None:
             if temptxt is None:
                 temptxt_fd, temptxt = tempfile.mkstemp(suffix='.txt')
-                try:
+                try:  # only in Python3 will this work
                     os.write(temptxt_fd, bytes(txt, 'UTF8'))
-                except TypeError:
+                except TypeError:  # oops, fall back to Python2
                     os.write(temptxt_fd, txt)
                 os.close(temptxt_fd)
             txt = '@' + temptxt
         else:
-            txt = '@%' + filename
+            # use a file instead of a text.
+            txt = "@%" + filename
+
         if size is not None:
-            size = '' if size[0] is None else str(size[0]), '' if size[1
-                ] is None else str(size[1])
-        cmd = [get_setting('IMAGEMAGICK_BINARY'), '-background', bg_color,
-            '-fill', color, '-font', font]
+            size = ('' if size[0] is None else str(size[0]),
+                    '' if size[1] is None else str(size[1]))
+
+        cmd = ([get_setting("IMAGEMAGICK_BINARY"),
+               "-background", bg_color,
+                "-fill", color,
+                "-font", font])
+
         if fontsize is not None:
-            cmd += ['-pointsize', '%d' % fontsize]
+            cmd += ["-pointsize", "%d" % fontsize]
         if kerning is not None:
-            cmd += ['-kerning', '%0.1f' % kerning]
+            cmd += ["-kerning", "%0.1f" % kerning]
         if stroke_color is not None:
-            cmd += ['-stroke', stroke_color, '-strokewidth', '%.01f' %
-                stroke_width]
+            cmd += ["-stroke", stroke_color, "-strokewidth",
+                    "%.01f" % stroke_width]
         if size is not None:
-            cmd += ['-size', '%sx%s' % (size[0], size[1])]
+            cmd += ["-size", "%sx%s" % (size[0], size[1])]
         if align is not None:
-            cmd += ['-gravity', align]
+            cmd += ["-gravity", align]
         if interline is not None:
-            cmd += ['-interline-spacing', '%d' % interline]
+            cmd += ["-interline-spacing", "%d" % interline]
+
         if tempfilename is None:
             tempfile_fd, tempfilename = tempfile.mkstemp(suffix='.png')
             os.close(tempfile_fd)
-        cmd += ['%s:%s' % (method, txt), '-type', 'truecolormatte', 
-            'PNG32:%s' % tempfilename]
+
+        cmd += ["%s:%s" % (method, txt),
+                "-type", "truecolormatte", "PNG32:%s" % tempfilename]
+
         if print_cmd:
-            print(' '.join(cmd))
+            print(" ".join(cmd))
+
         try:
             subprocess_call(cmd, logger=None)
         except (IOError, OSError) as err:
-            error = (
-                """MoviePy Error: creation of %s failed because of the following error:
-
-%s.
-
-."""
-                 % (filename, str(err)) +
-                "This error can be due to the fact that ImageMagick is not installed on your computer, or (for Windows users) that you didn't specify the path to the ImageMagick binary in file conf.py, or that the path you specified is incorrect"
-                )
+            error = ("MoviePy Error: creation of %s failed because of the "
+                     "following error:\n\n%s.\n\n." % (filename, str(err))
+                     + ("This error can be due to the fact that ImageMagick "
+                        "is not installed on your computer, or (for Windows "
+                        "users) that you didn't specify the path to the "
+                        "ImageMagick binary in file conf.py, or that the path "
+                        "you specified is incorrect"))
             raise IOError(error)
+
         ImageClip.__init__(self, tempfilename, transparent=transparent)
         self.txt = txt
         self.color = color
         self.stroke_color = stroke_color
+
         if remove_temp:
             if os.path.exists(tempfilename):
                 os.remove(tempfilename)
@@ -844,7 +1160,26 @@ class TextClip(ImageClip):
     def list(arg):
         """Returns the list of all valid entries for the argument of
         ``TextClip`` given (can be ``font``, ``color``, etc...) """
-        pass
+
+        popen_params = {"stdout": sp.PIPE,
+                        "stderr": DEVNULL,
+                        "stdin": DEVNULL}
+
+        if os.name == "nt":
+            popen_params["creationflags"] = 0x08000000
+
+        process = sp.Popen([get_setting("IMAGEMAGICK_BINARY"),
+                            '-list', arg], **popen_params)
+        result = process.communicate()[0]
+        lines = result.splitlines()
+
+        if arg == 'font':
+            return [l.decode('UTF-8')[8:] for l in lines if l.startswith(b"  Font:")]
+        elif arg == 'color':
+            return [l.split(b" ")[0] for l in lines[2:]]
+        else:
+            raise Exception("Moviepy:Error! Argument must equal "
+                            "'font' or 'color'")

     @staticmethod
     def search(string, arg):
@@ -855,4 +1190,6 @@ class TextClip(ImageClip):
            >>> print ( TextClip.search('Courier', 'font') )

         """
-        pass
+        string = string.lower()
+        names_list = TextClip.list(arg)
+        return [name for name in names_list if string in name.lower()]
diff --git a/moviepy/video/compositing/CompositeVideoClip.py b/moviepy/video/compositing/CompositeVideoClip.py
index fc3184d..080cd37 100644
--- a/moviepy/video/compositing/CompositeVideoClip.py
+++ b/moviepy/video/compositing/CompositeVideoClip.py
@@ -1,9 +1,12 @@
 import numpy as np
+
 from moviepy.audio.AudioClip import CompositeAudioClip
 from moviepy.video.VideoClip import ColorClip, VideoClip

+#  CompositeVideoClip

 class CompositeVideoClip(VideoClip):
+
     """ 

     A VideoClip made of other videoclips displayed together. This is the
@@ -43,22 +46,30 @@ class CompositeVideoClip(VideoClip):
     """

     def __init__(self, clips, size=None, bg_color=None, use_bgclip=False,
-        ismask=False):
+                 ismask=False):
+
         if size is None:
             size = clips[0].size
-        if use_bgclip and clips[0].mask is None:
+
+        
+        if use_bgclip and (clips[0].mask is None):
             transparent = False
         else:
-            transparent = bg_color is None
+            transparent = (bg_color is None)
+        
         if bg_color is None:
             bg_color = 0.0 if ismask else (0, 0, 0)
+
         fpss = [c.fps for c in clips if getattr(c, 'fps', None)]
         self.fps = max(fpss) if fpss else None
+
         VideoClip.__init__(self)
+        
         self.size = size
         self.ismask = ismask
         self.clips = clips
         self.bg_color = bg_color
+
         if use_bgclip:
             self.bg = clips[0]
             self.clips = clips[1:]
@@ -67,37 +78,61 @@ class CompositeVideoClip(VideoClip):
             self.clips = clips
             self.bg = ColorClip(size, color=self.bg_color)
             self.created_bg = True
+
+        
+        # compute duration
         ends = [c.end for c in self.clips]
         if None not in ends:
             duration = max(ends)
             self.duration = duration
             self.end = duration
+
+        # compute audio
         audioclips = [v.audio for v in self.clips if v.audio is not None]
         if audioclips:
             self.audio = CompositeAudioClip(audioclips)
+
+        # compute mask if necessary
         if transparent:
-            maskclips = [(c.mask if c.mask is not None else c.add_mask().
-                mask).set_position(c.pos).set_end(c.end).set_start(c.start,
-                change_end=False) for c in self.clips]
-            self.mask = CompositeVideoClip(maskclips, self.size, ismask=
-                True, bg_color=0.0)
+            maskclips = [(c.mask if (c.mask is not None) else
+                          c.add_mask().mask).set_position(c.pos)
+                          .set_end(c.end).set_start(c.start, change_end=False)
+                          for c in self.clips]
+
+            self.mask = CompositeVideoClip(maskclips,self.size, ismask=True,
+                                               bg_color=0.0)

         def make_frame(t):
             """ The clips playing at time `t` are blitted over one
                 another. """
+
             f = self.bg.get_frame(t)
             for c in self.playing_clips(t):
-                f = c.blit_on(f, t)
+                    f = c.blit_on(f, t)
             return f
+
         self.make_frame = make_frame

     def playing_clips(self, t=0):
         """ Returns a list of the clips in the composite clips that are
             actually playing at the given time `t`. """
-        pass
+        return [c for c in self.clips if c.is_playing(t)]
+
+    def close(self):
+        if self.created_bg and self.bg:
+            # Only close the background clip if it was locally created.
+            # Otherwise, it remains the job of whoever created it.
+            self.bg.close()
+            self.bg = None
+        if hasattr(self, "audio") and self.audio:
+            self.audio.close()
+            self.audio = None


-def clips_array(array, rows_widths=None, cols_widths=None, bg_color=None):
+
+def clips_array(array, rows_widths=None, cols_widths=None,
+                bg_color = None):
+
     """

     rows_widths
@@ -113,4 +148,29 @@ def clips_array(array, rows_widths=None, cols_widths=None, bg_color=None):
        regions to be transparent (will be slower).

     """
-    pass
+    
+    array = np.array(array)
+    sizes_array = np.array([[c.size for c in line] for line in array])
+    
+    # find row width and col_widths automatically if not provided
+    if rows_widths is None:
+        rows_widths = sizes_array[:,:,1].max(axis=1)
+    if cols_widths is None:
+        cols_widths = sizes_array[:,:,0].max(axis=0)
+    
+    xx = np.cumsum([0]+list(cols_widths)) 
+    yy = np.cumsum([0]+list(rows_widths))
+    
+    for j, (x, cw) in enumerate(zip(xx[:-1], cols_widths)):
+        for i, (y, rw) in enumerate(zip(yy[:-1], rows_widths)):
+            clip = array[i, j]
+            w, h = clip.size
+            if (w < cw) or (h < rw):
+                clip = (CompositeVideoClip([clip.set_position('center')],
+                                          size = (cw,rw),
+                                          bg_color = bg_color).
+                                     set_duration(clip.duration))
+                
+            array[i, j] = clip.set_position((x, y))
+                 
+    return CompositeVideoClip(array.flatten(), size=(xx[-1], yy[-1]), bg_color=bg_color)
diff --git a/moviepy/video/compositing/concatenate.py b/moviepy/video/compositing/concatenate.py
index 288c1e6..5352608 100644
--- a/moviepy/video/compositing/concatenate.py
+++ b/moviepy/video/compositing/concatenate.py
@@ -1,17 +1,19 @@
 import numpy as np
+
 from moviepy.audio.AudioClip import CompositeAudioClip
 from moviepy.tools import deprecated_version_of
 from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
 from moviepy.video.compositing.on_color import on_color
 from moviepy.video.VideoClip import ColorClip, VideoClip
-try:
-    reduce
-except NameError:
-    from functools import reduce
+
+try:               # Python 2
+   reduce
+except NameError:  # Python 3
+   from functools import reduce


-def concatenate_videoclips(clips, method='chain', transition=None, bg_color
-    =None, ismask=False, padding=0):
+def concatenate_videoclips(clips, method="chain", transition=None,
+                           bg_color=None, ismask=False, padding = 0):
     """ Concatenates several video clips

     Returns a video clip made by clip by concatenating several video clips.
@@ -60,8 +62,60 @@ def concatenate_videoclips(clips, method='chain', transition=None, bg_color
       `compose`.

     """
-    pass
+
+    if transition is not None:
+        l = [[v, transition] for v in clips[:-1]]
+        clips = reduce(lambda x, y: x + y, l) + [clips[-1]]
+        transition = None
+
+    tt = np.cumsum([0] + [c.duration for c in clips])
+
+    sizes = [v.size for v in clips]
+
+    w = max(r[0] for r in sizes)
+    h = max(r[1] for r in sizes)
+
+    tt = np.maximum(0, tt + padding * np.arange(len(tt)))
+
+    if method == "chain":
+        def make_frame(t):
+            i = max([i for i, e in enumerate(tt) if e <= t])
+            return clips[i].get_frame(t - tt[i])
+
+        def get_mask(c):
+            mask = c.mask or ColorClip([1, 1], color=1, ismask=True)
+            if mask.duration is None:
+               mask.duration = c.duration
+            return mask
+
+        result = VideoClip(ismask = ismask, make_frame = make_frame)
+        if any([c.mask is not None for c in clips]):
+            masks = [get_mask(c) for c in clips]
+            result.mask = concatenate_videoclips(masks, method="chain",
+                                                 ismask=True)
+            result.clips = clips
+    elif method == "compose":
+        result = CompositeVideoClip( [c.set_start(t).set_position('center')
+                                for (c, t) in zip(clips, tt)],
+               size = (w, h), bg_color=bg_color, ismask=ismask)
+    else:
+        raise Exception("Moviepy Error: The 'method' argument of "
+                        "concatenate_videoclips must be 'chain' or 'compose'")
+
+    result.tt = tt
+
+    result.start_times = tt[:-1]
+    result.start, result.duration, result.end = 0, tt[-1] , tt[-1]
+
+    audio_t = [(c.audio, t) for c, t in zip(clips,tt) if c.audio is not None]
+    if audio_t:
+        result.audio = CompositeAudioClip([a.set_start(t)
+                                for a,t in audio_t])
+
+    fpss = [c.fps for c in clips if getattr(c, 'fps', None) is not None]
+    result.fps = max(fpss) if fpss else None
+    return result


-concatenate = deprecated_version_of(concatenate_videoclips, oldname=
-    'concatenate')
+concatenate = deprecated_version_of(concatenate_videoclips,
+                                    oldname="concatenate")
diff --git a/moviepy/video/compositing/on_color.py b/moviepy/video/compositing/on_color.py
index 9f6c0bb..c5fcb14 100644
--- a/moviepy/video/compositing/on_color.py
+++ b/moviepy/video/compositing/on_color.py
@@ -14,4 +14,14 @@ def on_color(clip, size=None, color=(0, 0, 0), pos=None, col_opacity=None):
     :param pos: the position of the clip in the final clip.
     :param col_opacity: should the added zones be transparent ?
     """
-    pass
+    
+    if size is None:
+        size = clip.size
+    if pos is None:
+        pos = 'center'
+    colorclip = ColorClip(size, color=color)
+    if col_opacity:
+        colorclip = colorclip.with_mask().set_opacity(col_opacity)
+
+    return CompositeVideoClip([colorclip, clip.set_position(pos)],
+                              transparent=(col_opacity is not None))
diff --git a/moviepy/video/compositing/positioning.py b/moviepy/video/compositing/positioning.py
index 633f1d8..910e48b 100644
--- a/moviepy/video/compositing/positioning.py
+++ b/moviepy/video/compositing/positioning.py
@@ -1,3 +1,5 @@
 """
 This module provides classes that make positioning easy
 """
+
+# class ClipPosition:
diff --git a/moviepy/video/compositing/transitions.py b/moviepy/video/compositing/transitions.py
index 4a70b7f..db38151 100644
--- a/moviepy/video/compositing/transitions.py
+++ b/moviepy/video/compositing/transitions.py
@@ -3,9 +3,11 @@ Here is the current catalogue. These are meant
 to be used with clip.fx. There are available as transfx.crossfadein etc.
 if you load them with ``from moviepy.editor import *``
 """
+
 from moviepy.decorators import add_mask_if_none, requires_duration
 from moviepy.video.fx.fadein import fadein
 from moviepy.video.fx.fadeout import fadeout
+
 from .CompositeVideoClip import CompositeVideoClip


@@ -15,7 +17,10 @@ def crossfadein(clip, duration):
     """ Makes the clip appear progressively, over ``duration`` seconds.
     Only works when the clip is included in a CompositeVideoClip.
     """
-    pass
+    clip.mask.duration = clip.duration
+    newclip = clip.copy()
+    newclip.mask = clip.mask.fx(fadein, duration)
+    return newclip


 @requires_duration
@@ -24,7 +29,10 @@ def crossfadeout(clip, duration):
     """ Makes the clip disappear progressively, over ``duration`` seconds.
     Only works when the clip is included in a CompositeVideoClip.
     """
-    pass
+    clip.mask.duration = clip.duration
+    newclip = clip.copy()
+    newclip.mask = clip.mask.fx(fadeout, duration)
+    return newclip


 def slide_in(clip, duration, side):
@@ -57,7 +65,13 @@ def slide_in(clip, duration, side):
     >>> final_clip = concatenate( slided_clips, padding=-1)

     """
-    pass
+    w, h = clip.size
+    pos_dict = {'left': lambda t: (min(0, w*(t/duration-1)), 'center'),
+                'right': lambda t: (max(0, w*(1-t/duration)), 'center'),
+                'top': lambda t: ('center', min(0, h*(t/duration-1))),
+                'bottom': lambda t: ('center', max(0, h*(1-t/duration)))}
+
+    return clip.set_position(pos_dict[side])


 @requires_duration
@@ -91,7 +105,15 @@ def slide_out(clip, duration, side):
     >>> final_clip = concatenate( slided_clips, padding=-1)

     """
-    pass
+
+    w,h = clip.size
+    ts = clip.duration - duration # start time of the effect.
+    pos_dict = {'left' : lambda t: (min(0,w*(-(t-ts)/duration)),'center'),
+                'right' : lambda t: (max(0,w*((t-ts)/duration)),'center'),
+                'top' : lambda t: ('center',min(0,h*(-(t-ts)/duration))),
+                'bottom': lambda t: ('center',max(0,h*((t-ts)/duration))) }
+
+    return clip.set_position(pos_dict[side])


 @requires_duration
@@ -99,4 +121,6 @@ def make_loopable(clip, cross_duration):
     """ Makes the clip fade in progressively at its own end, this way
     it can be looped indefinitely. ``cross`` is the duration in seconds
     of the fade-in.  """
-    pass
+    d = clip.duration
+    clip2 = clip.fx(crossfadein, cross_duration).set_start(d - cross_duration)
+    return CompositeVideoClip([clip, clip2]).subclip(cross_duration, d)
diff --git a/moviepy/video/fx/accel_decel.py b/moviepy/video/fx/accel_decel.py
index 2a5708a..6fbb4cc 100644
--- a/moviepy/video/fx/accel_decel.py
+++ b/moviepy/video/fx/accel_decel.py
@@ -9,7 +9,14 @@ def f_accel_decel(t, old_d, new_d, abruptness=1, soonness=1.0):
       for positive abruptness, determines how soon the
       speedup occurs (0<soonness < inf)
     """
-    pass
+    
+    a = 1.0+abruptness
+    def _f(t):
+        f1 = lambda t: (0.5)**(1-a)*(t**a)
+        f2 = lambda t: (1-f1(1-t))
+        return (t<.5)*f1(t) + (t>=.5)*f2(t) 
+    
+    return old_d*_f((t/new_d)**soonness)


 def accel_decel(clip, new_duration=None, abruptness=1.0, soonness=1.0):
@@ -27,4 +34,11 @@ def accel_decel(clip, new_duration=None, abruptness=1.0, soonness=1.0):
       for positive abruptness, determines how soon the
       speedup occurs (0<soonness < inf)
     """
-    pass
+    
+    if new_duration is None:
+        new_duration = clip.duration
+    
+    fl = lambda t : f_accel_decel(t, clip.duration, new_duration,
+                                   abruptness, soonness)
+
+    return clip.fl_time(fl).set_duration(new_duration)
diff --git a/moviepy/video/fx/blackwhite.py b/moviepy/video/fx/blackwhite.py
index 1e5c886..b6b95a8 100644
--- a/moviepy/video/fx/blackwhite.py
+++ b/moviepy/video/fx/blackwhite.py
@@ -1,10 +1,22 @@
 import numpy as np


-def blackwhite(clip, RGB=None, preserve_luminosity=True):
+def blackwhite(clip, RGB = None, preserve_luminosity=True):
     """ Desaturates the picture, makes it black and white.
     Parameter RGB allows to set weights for the different color
     channels.
     If RBG is 'CRT_phosphor' a special set of values is used.
     preserve_luminosity maintains the sum of RGB to 1."""
-    pass
+    if RGB is None:
+        RGB = [1,1,1]
+
+    if RGB == 'CRT_phosphor':
+        RGB = [0.2125, 0.7154, 0.0721]
+
+    R,G,B = 1.0*np.array(RGB)/ (sum(RGB) if preserve_luminosity else 1)
+    
+    def fl(im):
+        im = (R*im[:,:,0] + G*im[:,:,1] + B*im[:,:,2])
+        return np.dstack(3*[im]).astype('uint8')
+
+    return clip.fl_image(fl)
diff --git a/moviepy/video/fx/blink.py b/moviepy/video/fx/blink.py
index dc6f2ce..c8a9624 100644
--- a/moviepy/video/fx/blink.py
+++ b/moviepy/video/fx/blink.py
@@ -7,4 +7,9 @@ def blink(clip, d_on, d_off):
     seconds and disappear ``d_off`` seconds. Will only work in
     composite clips.
     """
-    pass
+    newclip = copy.copy(clip)
+    if newclip.mask is None:
+        newclip = newclip.with_mask()
+    D = d_on + d_off
+    newclip.mask = newclip.mask.fl( lambda gf,t: gf(t)*((t % D) < d_on))
+    return newclip
diff --git a/moviepy/video/fx/colorx.py b/moviepy/video/fx/colorx.py
index 31863f5..70f16d6 100644
--- a/moviepy/video/fx/colorx.py
+++ b/moviepy/video/fx/colorx.py
@@ -6,4 +6,5 @@ def colorx(clip, factor):
         to decrease or increase the clip's brightness (is that the
         reight word ?)
     """
-    pass
+    return clip.fl_image( lambda pic: np.minimum(255,(factor*pic)).
+                                                        astype('uint8'))
diff --git a/moviepy/video/fx/crop.py b/moviepy/video/fx/crop.py
index cb20ab6..8066418 100644
--- a/moviepy/video/fx/crop.py
+++ b/moviepy/video/fx/crop.py
@@ -1,5 +1,4 @@
-def crop(clip, x1=None, y1=None, x2=None, y2=None, width=None, height=None,
-    x_center=None, y_center=None):
+def crop(clip, x1=None, y1=None, x2=None, y2=None, width=None, height=None, x_center=None, y_center=None):
     """
     Returns a new clip in which just a rectangular subregion of the
     original clip is conserved. x1,y1 indicates the top left corner and
@@ -29,4 +28,26 @@ def crop(clip, x1=None, y1=None, x2=None, y2=None, width=None, height=None,
     >>> crop(x_center=300, width=400, y1=100, y2=600)

     """
-    pass
+
+    if width and x1 is not None:
+        x2 = x1 + width
+    elif width and x2 is not None:
+        x1 = x2 - width
+
+    if height and y1 is not None:
+        y2 = y1 + height
+    elif height and y2 is not None:
+        y1 = y2 - height
+
+    if x_center:
+        x1, x2 = x_center - width / 2, x_center + width / 2
+
+    if y_center:
+        y1, y2 = y_center - height / 2, y_center + height / 2
+
+    x1 = x1 or 0
+    y1 = y1 or 0
+    x2 = x2 or clip.size[0]
+    y2 = y2 or clip.size[1]
+
+    return clip.fl_image(lambda pic: pic[int(y1) : int(y2), int(x1) : int(x2)], apply_to=["mask"])
diff --git a/moviepy/video/fx/even_size.py b/moviepy/video/fx/even_size.py
index 3ca8ea7..c7290b8 100644
--- a/moviepy/video/fx/even_size.py
+++ b/moviepy/video/fx/even_size.py
@@ -6,4 +6,17 @@ def even_size(clip):
     """ 
     Crops the clip to make dimensions even.
     """
-    pass
+    w, h = clip.size
+    w_even = w % 2 == 0
+    h_even = h % 2 == 0
+    if w_even and h_even:
+        return clip
+    
+    if not w_even and not h_even:
+        fl_image = lambda a : a[:-1,:-1,:]
+    elif w_even:
+        fl_image = lambda a : a[:,:-1,:]
+    else:
+        fl_image = lambda a : a[:-1,:,:]
+
+    return clip.fl_image(fl_image)
diff --git a/moviepy/video/fx/fadein.py b/moviepy/video/fx/fadein.py
index 3c91a46..c9fda92 100644
--- a/moviepy/video/fx/fadein.py
+++ b/moviepy/video/fx/fadein.py
@@ -9,4 +9,17 @@ def fadein(clip, duration, initial_color=None):
     For cross-fading (progressive appearance or disappearance of a clip
     over another clip, see ``composition.crossfade``
     """
-    pass
+
+    if initial_color is None:
+        initial_color = 0 if clip.ismask else [0,0,0]
+    
+    initial_color = np.array(initial_color)
+    
+    def fl(gf, t):
+        if t>=duration:
+            return gf(t)
+        else:
+            fading = (1.0*t/duration) 
+            return fading*gf(t) + (1-fading)*initial_color
+
+    return clip.fl(fl)
diff --git a/moviepy/video/fx/fadeout.py b/moviepy/video/fx/fadeout.py
index 0475495..66c7230 100644
--- a/moviepy/video/fx/fadeout.py
+++ b/moviepy/video/fx/fadeout.py
@@ -1,4 +1,5 @@
 import numpy as np
+
 from moviepy.decorators import requires_duration


@@ -11,4 +12,17 @@ def fadeout(clip, duration, final_color=None):
     For cross-fading (progressive appearance or disappearance of a clip
     over another clip, see ``composition.crossfade``
     """
-    pass
+    
+    if final_color is None:
+        final_color = 0 if clip.ismask else [0,0,0]
+    
+    final_color = np.array(final_color)
+
+    def fl(gf, t):
+        if (clip.duration-t)>=duration:
+            return gf(t)
+        else:
+            fading = 1.0 * (clip.duration - t) / duration
+            return fading*gf(t) + (1-fading)*final_color
+
+    return clip.fl(fl)
diff --git a/moviepy/video/fx/freeze.py b/moviepy/video/fx/freeze.py
index cd36a63..ff4d4aa 100644
--- a/moviepy/video/fx/freeze.py
+++ b/moviepy/video/fx/freeze.py
@@ -4,8 +4,8 @@ from moviepy.video.VideoClip import ImageClip


 @requires_duration
-def freeze(clip, t=0, freeze_duration=None, total_duration=None, padding_end=0
-    ):
+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
@@ -15,4 +15,15 @@ def freeze(clip, t=0, freeze_duration=None, total_duration=None, padding_end=0
     the clip and the freeze (i.e. the duration of the freeze is
     automatically calculated). One of them must be provided.
     """
-    pass
+
+    if t=='end':
+        t = clip.duration - padding_end
+
+    if freeze_duration is None:
+        freeze_duration = total_duration - clip.duration
+
+    before = [clip.subclip(0,t)] if (t!=0) else []
+    freeze = [clip.to_ImageClip(t).set_duration(freeze_duration)]
+    after = [clip.subclip(t)] if (t !=clip.duration) else []
+    return concatenate_videoclips(before + freeze + after)
+  
\ No newline at end of file
diff --git a/moviepy/video/fx/freeze_region.py b/moviepy/video/fx/freeze_region.py
index f39e4d1..a8ec7cf 100644
--- a/moviepy/video/fx/freeze_region.py
+++ b/moviepy/video/fx/freeze_region.py
@@ -1,8 +1,10 @@
 from moviepy.decorators import apply_to_mask
 from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
+
 from .crop import crop


+#@apply_to_mask
 def freeze_region(clip, t=0, region=None, outside_region=None, mask=None):
     """ Freezes one region of the clip while the rest remains animated.

@@ -29,4 +31,27 @@ def freeze_region(clip, t=0, region=None, outside_region=None, mask=None):
       indicate the freezed region in the final picture.

     """
-    pass
+    
+    if region is not None:
+
+        x1, y1, x2, y2 = region
+        freeze = (clip.fx(crop, *region)
+                      .to_ImageClip(t=t)
+                      .set_duration(clip.duration)
+                      .set_position((x1,y1)))
+        return CompositeVideoClip([clip, freeze])
+    
+    elif outside_region is not None:
+        
+        x1, y1, x2, y2 = outside_region
+        animated_region = (clip.fx(crop, *outside_region)
+                               .set_position((x1,y1)))
+        freeze = (clip.to_ImageClip(t=t)
+                      .set_duration(clip.duration))
+        return CompositeVideoClip([freeze, animated_region])
+    
+    elif mask is not None:
+        freeze = (clip.to_ImageClip(t=t)
+                      .set_duration(clip.duration)
+                      .set_mask(mask))
+        return CompositeVideoClip([clip, freeze])
diff --git a/moviepy/video/fx/gamma_corr.py b/moviepy/video/fx/gamma_corr.py
index 7b3cfef..5668698 100644
--- a/moviepy/video/fx/gamma_corr.py
+++ b/moviepy/video/fx/gamma_corr.py
@@ -1,3 +1,8 @@
+
 def gamma_corr(clip, gamma):
     """ Gamma-correction of a video clip """
-    pass
+    def fl(im):
+        corrected = (255*(1.0*im/255)**gamma)
+        return corrected.astype('uint8')
+    
+    return clip.fl_image(fl)
diff --git a/moviepy/video/fx/headblur.py b/moviepy/video/fx/headblur.py
index 98c97ec..2d46d5a 100644
--- a/moviepy/video/fx/headblur.py
+++ b/moviepy/video/fx/headblur.py
@@ -1,14 +1,17 @@
 import numpy as np
+
+#------- CHECKING DEPENDENCIES ----------------------------------------- 
 try:
     import cv2
     headblur_possible = True
     if cv2.__version__ >= '3.0.0':
-        cv2.CV_AA = cv2.LINE_AA
+       cv2.CV_AA=cv2.LINE_AA
 except:
     headblur_possible = False
+#-----------------------------------------------------------------------


-def headblur(clip, fx, fy, r_zone, r_blur=None):
+def headblur(clip,fx,fy,r_zone,r_blur=None):
     """
     Returns a filter that will blurr a moving part (a head ?) of
     the frames. The position of the blur at time t is
@@ -18,9 +21,38 @@ def headblur(clip, fx, fy, r_zone, r_blur=None):
     Automatically deals with the case where part of the image goes
     offscreen.
     """
-    pass
+    
+    if r_blur is None: r_blur = 2*r_zone/3
+    
+    def fl(gf,t):
+        
+        im = gf(t)
+        h,w,d = im.shape
+        x,y = int(fx(t)),int(fy(t))
+        x1,x2 = max(0,x-r_zone),min(x+r_zone,w)
+        y1,y2 = max(0,y-r_zone),min(y+r_zone,h)
+        region_size = y2-y1,x2-x1
+        
+        mask = np.zeros(region_size).astype('uint8')
+        cv2.circle(mask, (r_zone,r_zone), r_zone, 255, -1,
+                   lineType=cv2.CV_AA)
+                               
+        mask = np.dstack(3*[(1.0/255)*mask])
+        
+        orig = im[y1:y2, x1:x2]
+        blurred = cv2.blur(orig,(r_blur, r_blur))
+        im[y1:y2, x1:x2] = mask*blurred + (1-mask)*orig
+        return im
+    
+    return clip.fl(fl)
+


+#------- OVERWRITE IF REQUIREMENTS NOT MET -----------------------------
 if not headblur_possible:
     doc = headblur.__doc__
+    def headblur(clip,fx,fy,r_zone,r_blur=None):
+        raise IOError("fx painting needs opencv")
+    
     headblur.__doc__ = doc
+#----------------------------------------------------------------------- 
diff --git a/moviepy/video/fx/invert_colors.py b/moviepy/video/fx/invert_colors.py
index fc124ac..0fb948e 100644
--- a/moviepy/video/fx/invert_colors.py
+++ b/moviepy/video/fx/invert_colors.py
@@ -4,4 +4,5 @@ def invert_colors(clip):
     The values of all pixels are replaced with (255-v) or (1-v) for masks 
     Black becomes white, green becomes purple, etc.
     """
-    pass
+    maxi = (1.0 if clip.ismask else 255)
+    return clip.fl_image(lambda f : maxi - f)
diff --git a/moviepy/video/fx/loop.py b/moviepy/video/fx/loop.py
index e76eb1f..b0c04be 100644
--- a/moviepy/video/fx/loop.py
+++ b/moviepy/video/fx/loop.py
@@ -18,4 +18,9 @@ def loop(self, n=None, duration=None):
     duration
       Total duration of the clip. Can be specified instead of n.
     """
-    pass
+    result = self.fl_time(lambda t: t % self.duration)
+    if n:
+        duration = n*self.duration
+    if duration:
+        result = result.set_duration(duration)
+    return result
diff --git a/moviepy/video/fx/lum_contrast.py b/moviepy/video/fx/lum_contrast.py
index 9473d5a..a1ae55c 100644
--- a/moviepy/video/fx/lum_contrast.py
+++ b/moviepy/video/fx/lum_contrast.py
@@ -1,3 +1,11 @@
-def lum_contrast(clip, lum=0, contrast=0, contrast_thr=127):
+def lum_contrast(clip, lum = 0, contrast=0, contrast_thr=127):
     """ luminosity-contrast correction of a clip """
-    pass
+    
+    def fl_image(im):
+        im = 1.0*im # float conversion
+        corrected = im + lum + contrast*(im-float(contrast_thr))
+        corrected[corrected < 0] = 0
+        corrected[corrected > 255] = 255
+        return corrected.astype('uint8')
+    
+    return clip.fl_image(fl_image)
diff --git a/moviepy/video/fx/make_loopable.py b/moviepy/video/fx/make_loopable.py
index 269c25e..485887c 100644
--- a/moviepy/video/fx/make_loopable.py
+++ b/moviepy/video/fx/make_loopable.py
@@ -6,5 +6,9 @@ def make_loopable(clip, cross):
     """
     Makes the clip fade in progressively at its own end, this way
     it can be looped indefinitely. ``cross`` is the duration in seconds
-    of the fade-in.  """
-    pass
+    of the fade-in.  """  
+    d = clip.duration
+    clip2 = clip.fx(transfx.crossfadein, cross).\
+                 set_start(d - cross)
+    return CompositeVideoClip([ clip, clip2 ]).\
+                 subclip(cross,d)
diff --git a/moviepy/video/fx/margin.py b/moviepy/video/fx/margin.py
index 3b4e36b..20dd193 100644
--- a/moviepy/video/fx/margin.py
+++ b/moviepy/video/fx/margin.py
@@ -1,11 +1,12 @@
 import numpy as np
+
 from moviepy.decorators import apply_to_mask
 from moviepy.video.VideoClip import ImageClip


 @apply_to_mask
-def margin(clip, mar=None, left=0, right=0, top=0, bottom=0, color=(0, 0, 0
-    ), opacity=1.0):
+def margin(clip, mar=None, left=0, right=0, top=0,
+           bottom=0, color=(0, 0, 0), opacity = 1.0):
     """
     Draws an external margin all around the frame.

@@ -21,4 +22,37 @@ def margin(clip, mar=None, left=0, right=0, top=0, bottom=0, color=(0, 0, 0
         this value to 0 yields transparent margins.

     """
-    pass
+
+    if (opacity != 1.0) and (clip.mask is None) and not (clip.ismask):
+        clip = clip.add_mask()
+
+    if mar is not None:
+        left = right = top = bottom = mar
+    
+    def make_bg(w,h):
+        new_w, new_h = w + left + right, h + top + bottom
+        if clip.ismask:
+            shape = (new_h, new_w)
+            bg = ( np.tile(opacity, (new_h, new_w))
+                       .astype(float)
+                       .reshape(shape))
+        else:
+            shape = (new_h, new_w, 3)
+            bg = np.tile(color, (new_h, new_w)).reshape(shape)
+        return bg
+        
+    if isinstance(clip, ImageClip):
+        
+        im =  make_bg(clip.w,clip.h)
+        im[top:top + clip.h, left:left + clip.w] = clip.img
+        return clip.fl_image(lambda pic:im)
+        
+    else:
+        
+        def fl(gf, t):
+            pic = gf(t)
+            h,w = pic.shape[:2]
+            im = make_bg(w,h)
+            im[top:top + h, left:left + w] = pic
+            return im
+        return clip.fl(fl)
diff --git a/moviepy/video/fx/mask_and.py b/moviepy/video/fx/mask_and.py
index a42f22f..ccec602 100644
--- a/moviepy/video/fx/mask_and.py
+++ b/moviepy/video/fx/mask_and.py
@@ -1,4 +1,5 @@
 import numpy as np
+
 from ..VideoClip import ImageClip


@@ -7,4 +8,13 @@ def mask_and(clip, other_clip):
         other_clip can be a mask clip or a picture (np.array).
         The result has the duration of 'clip' (if it has any)
     """
-    pass
+
+    # To ensure that 'or' of two ImageClips will be an ImageClip.
+    if isinstance(other_clip, ImageClip):
+       other_clip = other_clip.img
+
+    if isinstance(other_clip, np.ndarray):
+       return clip.fl_image(lambda f : np.minimum(f, other_clip))
+    else:
+       return clip.fl(lambda gf, t : np.minimum(gf(t),
+                                                other_clip.get_frame(t)))
diff --git a/moviepy/video/fx/mask_color.py b/moviepy/video/fx/mask_color.py
index 7a8a2e6..fba87dd 100644
--- a/moviepy/video/fx/mask_color.py
+++ b/moviepy/video/fx/mask_color.py
@@ -14,4 +14,21 @@ def mask_color(clip, color=None, thr=0, s=1):
     which is 1 when d>>thr and 0 for d<<thr, the stiffness of the effect being
     parametrized by s
     """
-    pass
+    if color is None:
+        color = [0,0,0]
+
+    color = np.array(color)
+
+    def hill(x):
+        if thr:
+            return x**s / (thr**s + x**s)
+        else:
+            return 1.0 * (x != 0) 
+    
+    def flim(im): 
+        return hill(np.sqrt(((im-color)**2).sum(axis=2)))
+    
+    mask = clip.fl_image(flim)
+    mask.ismask= True
+    newclip = clip.set_mask(mask)
+    return newclip
diff --git a/moviepy/video/fx/mask_or.py b/moviepy/video/fx/mask_or.py
index 249ec6a..abe5e74 100644
--- a/moviepy/video/fx/mask_or.py
+++ b/moviepy/video/fx/mask_or.py
@@ -1,4 +1,5 @@
 import numpy as np
+
 from ..VideoClip import ImageClip


@@ -7,4 +8,13 @@ def mask_or(clip, other_clip):
         other_clip can be a mask clip or a picture (np.array).
         The result has the duration of 'clip' (if it has any)
     """
-    pass
+
+    # To ensure that 'or' of two ImageClips will be an ImageClip.
+    if isinstance(other_clip, ImageClip):
+       other_clip = other_clip.img
+
+    if isinstance(other_clip, np.ndarray):
+       return clip.fl_image(lambda f : np.maximum(f, other_clip))
+    else:
+       return clip.fl(lambda gf, t : np.maximum(gf(t),
+                                                 other_clip.get_frame(t)))
diff --git a/moviepy/video/fx/mirror_x.py b/moviepy/video/fx/mirror_x.py
index 8577e02..3173dac 100644
--- a/moviepy/video/fx/mirror_x.py
+++ b/moviepy/video/fx/mirror_x.py
@@ -1,3 +1,4 @@
-def mirror_x(clip, apply_to='mask'):
+
+def mirror_x(clip, apply_to= "mask"):
     """ flips the clip horizontally (and its mask too, by default) """
-    pass
+    return clip.fl_image(lambda f: f[:,::-1], apply_to = apply_to)
diff --git a/moviepy/video/fx/mirror_y.py b/moviepy/video/fx/mirror_y.py
index b61b15c..0a602e9 100644
--- a/moviepy/video/fx/mirror_y.py
+++ b/moviepy/video/fx/mirror_y.py
@@ -1,3 +1,3 @@
-def mirror_y(clip, apply_to='mask'):
+def mirror_y(clip, apply_to= "mask"):
     """ flips the clip vertically (and its mask too, by default) """
-    pass
+    return clip.fl_image(lambda f : f[::-1], apply_to = apply_to)
diff --git a/moviepy/video/fx/painting.py b/moviepy/video/fx/painting.py
index 03236f0..b41bac3 100644
--- a/moviepy/video/fx/painting.py
+++ b/moviepy/video/fx/painting.py
@@ -1,3 +1,4 @@
+#------- CHECKING DEPENDENCIES ----------------------------------------- 
 painting_possible = True
 try:
     from skimage.filter import sobel
@@ -6,24 +7,37 @@ except:
         from scipy.ndimage.filters import sobel
     except:
         painting_possible = False
-import numpy as np
+#-----------------------------------------------------------------------    


-def to_painting(image, saturation=1.4, black=0.006):
-    """ transforms any photo into some kind of painting """
-    pass
+
+import numpy as np


-def painting(clip, saturation=1.4, black=0.006):
+def to_painting(image,saturation = 1.4,black = 0.006):
+    """ transforms any photo into some kind of painting """
+    edges = sobel(image.mean(axis=2))
+    darkening =  black*(255*np.dstack(3*[edges]))
+    painting = saturation*image-darkening
+    return np.maximum(0,np.minimum(255,painting)).astype('uint8')
+    
+def painting(clip, saturation = 1.4,black = 0.006):
     """
     Transforms any photo into some kind of painting. Saturation
     tells at which point the colors of the result should be
     flashy. ``black`` gives the anount of black lines wanted.
     Requires Scikit-image or Scipy installed.
     """
-    pass
+    return clip.fl_image(lambda im : to_painting(im,saturation,black))
+        
+

+#------- OVERWRITE IF REQUIREMENTS NOT MET -----------------------------

 if not painting_possible:
     doc = painting.__doc__
+    def painting(clip, newsize=None, height=None, width=None):
+        raise IOError("fx painting needs scikit-image or scipy")
+    
     painting.__doc__ = doc
+#----------------------------------------------------------------------- 
diff --git a/moviepy/video/fx/resize.py b/moviepy/video/fx/resize.py
index 00274b3..8636e74 100644
--- a/moviepy/video/fx/resize.py
+++ b/moviepy/video/fx/resize.py
@@ -1,21 +1,60 @@
 resize_possible = True
+
 try:
+    # TRY USING OpenCV AS RESIZER
+    #raise ImportError #debugging
     import cv2
     import numpy as np
-    resizer.origin = 'cv2'
+    def resizer (pic, newsize):
+        lx, ly = int(newsize[0]), int(newsize[1])
+        if lx > pic.shape[1] or ly > pic.shape[0]:
+            # For upsizing use linear for good quality & decent speed
+            interpolation = cv2.INTER_LINEAR
+        else:
+            # For dowsizing use area to prevent aliasing
+            interpolation = cv2.INTER_AREA
+        return cv2.resize(+pic.astype('uint8'), (lx, ly),
+                          interpolation=interpolation)
+
+    resizer.origin = "cv2"
+                
 except ImportError:
+    
+    
     try:
+        # TRY USING PIL/PILLOW AS RESIZER
         from PIL import Image
         import numpy as np
-        resizer.origin = 'PIL'
+        def resizer(pic, newsize):
+            newsize = list(map(int, newsize))[::-1]
+            shape = pic.shape
+            if len(shape)==3:
+                newshape = (newsize[0],newsize[1], shape[2] )
+            else:
+                newshape = (newsize[0],newsize[1])
+                
+            pilim = Image.fromarray(pic)
+            resized_pil = pilim.resize(newsize[::-1], Image.ANTIALIAS)
+            #arr = np.fromstring(resized_pil.tostring(), dtype='uint8')
+            #arr.reshape(newshape)
+            return np.array(resized_pil)
+            
+        resizer.origin = "PIL"
+            
     except ImportError:
+        # TRY USING SCIPY AS RESIZER
         try:
             from scipy.misc import imresize
-            resizer = lambda pic, newsize: imresize(pic, map(int, newsize[:
-                :-1]))
-            resizer.origin = 'Scipy'
+            resizer = lambda pic, newsize : imresize(pic,
+                                            map(int, newsize[::-1]))
+            resizer.origin = "Scipy"
+                                               
         except ImportError:
             resize_possible = False
+            
+        
+        
+    
 from moviepy.decorators import apply_to_mask


@@ -49,9 +88,78 @@ def resize(clip, newsize=None, height=None, width=None, apply_to_mask=True):
     >>> myClip.resize(lambda t : 1+0.02*t) # slow swelling of the clip

     """
-    pass
+
+    w, h = clip.size
+    
+    if newsize is not None:
+        
+        def trans_newsize(ns):
+            
+            if isinstance(ns, (int, float)):
+                return [ns * w, ns * h]
+            else:
+                return ns
+                
+        if hasattr(newsize, "__call__"):
+            
+            newsize2 = lambda t : trans_newsize(newsize(t))
+            
+            if clip.ismask:
+                
+                fun = lambda gf,t: (1.0*resizer((255 * gf(t)).astype('uint8'),
+                                                 newsize2(t))/255)
+            else:
+                
+                fun = lambda gf,t: resizer(gf(t).astype('uint8'),
+                                          newsize2(t))
+                
+            return clip.fl(fun, keep_duration=True,
+                           apply_to= (["mask"] if apply_to_mask else []))
+            
+        else:
+            
+            newsize = trans_newsize(newsize)
+        
+
+    elif height is not None:
+        
+        if hasattr(height, "__call__"):
+            fun = lambda t : 1.0*int(height(t))/h
+            return resize(clip, fun)
+
+
+        else:
+
+            newsize = [w * height / h, height]
+        
+    elif width is not None:
+
+        if hasattr(width, "__call__"):
+            fun = lambda t : 1.0*width(t)/w
+            return resize(clip, fun)
+        
+        newsize = [width, h * width / w]
+        
+        
+    # From here, the resizing is constant (not a function of time), size=newsize
+
+    if clip.ismask:
+        fl = lambda pic: 1.0*resizer((255 * pic).astype('uint8'), newsize)/255.0
+            
+    else:
+        fl = lambda pic: resizer(pic.astype('uint8'), newsize)
+
+    newclip = clip.fl_image(fl)
+
+    if apply_to_mask and clip.mask is not None:
+        newclip.mask = resize(clip.mask, newsize, apply_to_mask=False)
+
+    return newclip


 if not resize_possible:
+    
     doc = resize.__doc__
+    def resize(clip, newsize=None, height=None, width=None):
+        raise ImportError("fx resize needs OpenCV or Scipy or PIL")
     resize.__doc__ = doc
diff --git a/moviepy/video/fx/rotate.py b/moviepy/video/fx/rotate.py
index 973879e..da22268 100644
--- a/moviepy/video/fx/rotate.py
+++ b/moviepy/video/fx/rotate.py
@@ -1,13 +1,17 @@
 import numpy as np
+
 from moviepy.decorators import apply_to_mask
+
 try:
     from PIL import Image
     PIL_FOUND = True
+    def pil_rotater(pic, angle, resample, expand):
+        return np.array( Image.fromarray(pic).rotate(angle, expand=expand,
+                                                     resample=resample))
 except ImportError:
     PIL_FOUND = False

-
-def rotate(clip, angle, unit='deg', resample='bicubic', expand=True):
+def rotate(clip, angle, unit='deg', resample="bicubic", expand=True):
     """
     Change unit to 'rad' to define angles as radians.
     If the angle is not one of 90, 180, -90, -180 (degrees) there will be
@@ -33,4 +37,37 @@ def rotate(clip, angle, unit='deg', resample='bicubic', expand=True):
     expand
       Only applIf False, the clip will maintain the same True, the clip will be resized so that the whole
     """
-    pass
+    
+    resample = {"bilinear": Image.BILINEAR,
+                "nearest": Image.NEAREST,
+                "bicubic": Image.BICUBIC}[resample]
+
+    if not hasattr(angle, '__call__'):
+        # if angle is a constant, convert to a constant function
+        a = +angle
+        angle = lambda t: a
+
+    transpo = [1,0] if clip.ismask else [1,0,2]
+
+    def fl(gf, t):
+
+        a = angle(t)
+        im = gf(t)
+
+        if unit == 'rad':
+            a = 360.0*a/(2*np.pi)
+        
+        if (a==90) and expand:
+            return np.transpose(im, axes=transpo)[::-1]
+        elif (a==-90) and expand:
+            return np.transpose(im, axes=transpo)[:,::-1]
+        elif (a in [180, -180]) and expand:
+            return im[::-1,::-1]
+        elif not PIL_FOUND:
+            raise ValueError('Without "Pillow" installed, only angles 90, -90,'
+                             '180 are supported, please install "Pillow" with'
+                             "pip install pillow")
+        else:
+            return pil_rotater(im, a, resample=resample, expand=expand)
+
+    return clip.fl(fl, apply_to=["mask"])
diff --git a/moviepy/video/fx/scroll.py b/moviepy/video/fx/scroll.py
index 4a2c40a..34f4f40 100644
--- a/moviepy/video/fx/scroll.py
+++ b/moviepy/video/fx/scroll.py
@@ -1,5 +1,16 @@
-def scroll(clip, h=None, w=None, x_speed=0, y_speed=0, x_start=0, y_start=0,
-    apply_to='mask'):
+def scroll(clip, h=None, w=None, x_speed=0, y_speed=0,
+           x_start=0, y_start=0, apply_to="mask"):
     """ Scrolls horizontally or vertically a clip, e.g. to make end
         credits """
-    pass
+    if h is None: h = clip.h
+    if w is None: w = clip.w
+    
+    xmax = clip.w-w-1
+    ymax = clip.h-h-1
+
+    def f(gf,t):
+        x = int(max(0, min(xmax, x_start+ round(x_speed*t))))
+        y = int(max(0, min(ymax, y_start+ round(y_speed*t))))
+        return gf(t)[y:y+h, x:x+w]
+    
+    return clip.fl(f, apply_to = apply_to)
diff --git a/moviepy/video/fx/speedx.py b/moviepy/video/fx/speedx.py
index ee902f1..1c6b82c 100644
--- a/moviepy/video/fx/speedx.py
+++ b/moviepy/video/fx/speedx.py
@@ -1,7 +1,7 @@
 from moviepy.decorators import apply_to_audio, apply_to_mask


-def speedx(clip, factor=None, final_duration=None):
+def speedx(clip, factor = None, final_duration=None):
     """
     Returns a clip playing the current clip but at a speed multiplied
     by ``factor``. Instead of factor one can indicate the desired
@@ -9,4 +9,13 @@ def speedx(clip, factor=None, final_duration=None):
     computed.
     The same effect is applied to the clip's audio and mask if any.
     """
-    pass
+    
+    if final_duration:
+        factor = 1.0* clip.duration / final_duration
+        
+    newclip = clip.fl_time(lambda t: factor * t, apply_to=['mask', 'audio'])
+    
+    if clip.duration is not None:
+        newclip = newclip.set_duration(1.0 * clip.duration / factor)
+    
+    return newclip
diff --git a/moviepy/video/fx/supersample.py b/moviepy/video/fx/supersample.py
index e09615f..93b9d7a 100644
--- a/moviepy/video/fx/supersample.py
+++ b/moviepy/video/fx/supersample.py
@@ -4,4 +4,10 @@ import numpy as np
 def supersample(clip, d, nframes):
     """ Replaces each frame at time t by the mean of `nframes` equally spaced frames
     taken in the interval [t-d, t+d]. This results in motion blur."""
-    pass
+    
+    def fl(gf, t):
+        tt = np.linspace(t-d, t+d, nframes)
+        avg = np.mean(1.0*np.array([gf(t_) for t_ in tt], dtype='uint16'), axis=0)
+        return avg.astype("uint8")
+
+    return clip.fl(fl)
diff --git a/moviepy/video/fx/time_mirror.py b/moviepy/video/fx/time_mirror.py
index 92edbdb..1c044e3 100644
--- a/moviepy/video/fx/time_mirror.py
+++ b/moviepy/video/fx/time_mirror.py
@@ -10,4 +10,4 @@ def time_mirror(self):
     The clip must have its ``duration`` attribute set.
     The same effect is applied to the clip's audio and mask if any.
     """
-    pass
+    return self.fl_time(lambda t: self.duration - t, keep_duration=True)
diff --git a/moviepy/video/fx/time_symmetrize.py b/moviepy/video/fx/time_symmetrize.py
index 7b9e089..de2f318 100644
--- a/moviepy/video/fx/time_symmetrize.py
+++ b/moviepy/video/fx/time_symmetrize.py
@@ -1,5 +1,6 @@
 from moviepy.decorators import apply_to_audio, apply_to_mask, requires_duration
 from moviepy.video.compositing.concatenate import concatenate_videoclips
+
 from .time_mirror import time_mirror


@@ -13,4 +14,4 @@ def time_symmetrize(clip):
     This effect is automatically applied to the clip's mask and audio
     if they exist.
     """
-    pass
+    return concatenate_videoclips([clip, clip.fx( time_mirror )])
diff --git a/moviepy/video/io/ImageSequenceClip.py b/moviepy/video/io/ImageSequenceClip.py
index de83c85..662138c 100644
--- a/moviepy/video/io/ImageSequenceClip.py
+++ b/moviepy/video/io/ImageSequenceClip.py
@@ -1,6 +1,8 @@
 import os
+
 import numpy as np
 from imageio import imread
+
 from ..VideoClip import VideoClip


@@ -44,88 +46,119 @@ class ImageSequenceClip(VideoClip):

     """

+
     def __init__(self, sequence, fps=None, durations=None, with_mask=True,
-        ismask=False, load_images=False):
-        if fps is None and durations is None:
+                 ismask=False, load_images=False):
+
+        # CODE WRITTEN AS IT CAME, MAY BE IMPROVED IN THE FUTURE
+
+        if (fps is None) and (durations is None):
             raise ValueError("Please provide either 'fps' or 'durations'.")
         VideoClip.__init__(self, ismask=ismask)
+
+        # Parse the data
+
         fromfiles = True
+
         if isinstance(sequence, list):
             if isinstance(sequence[0], str):
                 if load_images:
                     sequence = [imread(f) for f in sequence]
                     fromfiles = False
                 else:
-                    fromfiles = True
+                    fromfiles= True
             else:
+                # sequence is already a list of numpy arrays
                 fromfiles = False
         else:
+            # sequence is a folder name, make it a list of files:
             fromfiles = True
-            sequence = sorted([os.path.join(sequence, f) for f in os.
-                listdir(sequence)])
+            sequence = sorted([os.path.join(sequence, f)
+                        for f in os.listdir(sequence)])
+
+
+        #check that all the images are of the same size
         if isinstance(sequence[0], str):
-            size = imread(sequence[0]).shape
+           size = imread(sequence[0]).shape
         else:
-            size = sequence[0].shape
+           size = sequence[0].shape
+
         for image in sequence:
-            image1 = image
+            image1=image
             if isinstance(image, str):
-                image1 = imread(image)
+               image1=imread(image)
             if size != image1.shape:
-                raise Exception(
-                    'Moviepy: ImageSequenceClip requires all images to be the same size'
-                    )
+               raise Exception("Moviepy: ImageSequenceClip requires all images to be the same size")
+
+
         self.fps = fps
         if fps is not None:
-            durations = [(1.0 / fps) for image in sequence]
-            self.images_starts = [(1.0 * i / fps - np.finfo(np.float32).eps
-                ) for i in range(len(sequence))]
+            durations = [1.0/fps for image in sequence]
+            self.images_starts = [1.0*i/fps-np.finfo(np.float32).eps for i in range(len(sequence))]
         else:
-            self.images_starts = [0] + list(np.cumsum(durations))
+            self.images_starts = [0]+list(np.cumsum(durations))
         self.durations = durations
         self.duration = sum(durations)
         self.end = self.duration
         self.sequence = sequence
-
+        
         def find_image_index(t):
-            return max([i for i in range(len(self.sequence)) if self.
-                images_starts[i] <= t])
+            return max([i for i in range(len(self.sequence))
+                              if self.images_starts[i]<=t])
+
         if fromfiles:
+
             self.lastindex = None
             self.lastimage = None

             def make_frame(t):
+            
                 index = find_image_index(t)
+
                 if index != self.lastindex:
-                    self.lastimage = imread(self.sequence[index])[:, :, :3]
+                    self.lastimage = imread(self.sequence[index])[:,:,:3] 
                     self.lastindex = index
+                
                 return self.lastimage
-            if with_mask and imread(self.sequence[0]).shape[2] == 4:
+
+            if with_mask and (imread(self.sequence[0]).shape[2]==4):
+
                 self.mask = VideoClip(ismask=True)
                 self.mask.lastindex = None
                 self.mask.lastimage = None

                 def mask_make_frame(t):
+            
                     index = find_image_index(t)
                     if index != self.mask.lastindex:
-                        frame = imread(self.sequence[index])[:, :, 3]
-                        self.mask.lastimage = frame.astype(float) / 255
+                        frame = imread(self.sequence[index])[:,:,3]
+                        self.mask.lastimage = frame.astype(float)/255
                         self.mask.lastindex = index
+
                     return self.mask.lastimage
+
                 self.mask.make_frame = mask_make_frame
                 self.mask.size = mask_make_frame(0).shape[:2][::-1]
+
+
         else:

             def make_frame(t):
+            
                 index = find_image_index(t)
-                return self.sequence[index][:, :, :3]
-            if with_mask and self.sequence[0].shape[2] == 4:
+                return self.sequence[index][:,:,:3]
+
+            if with_mask and (self.sequence[0].shape[2]==4):
+
                 self.mask = VideoClip(ismask=True)

                 def mask_make_frame(t):
                     index = find_image_index(t)
-                    return 1.0 * self.sequence[index][:, :, 3] / 255
+                    return 1.0*self.sequence[index][:,:,3]/255
+
                 self.mask.make_frame = mask_make_frame
                 self.mask.size = mask_make_frame(0).shape[:2][::-1]
+        
+            
         self.make_frame = make_frame
         self.size = make_frame(0).shape[:2][::-1]
diff --git a/moviepy/video/io/VideoFileClip.py b/moviepy/video/io/VideoFileClip.py
index 5bb4b1f..1d5c8b6 100644
--- a/moviepy/video/io/VideoFileClip.py
+++ b/moviepy/video/io/VideoFileClip.py
@@ -1,4 +1,5 @@
 import os
+
 from moviepy.audio.io.AudioFileClip import AudioFileClip
 from moviepy.Clip import Clip
 from moviepy.video.io.ffmpeg_reader import FFMPEG_VideoReader
@@ -6,6 +7,7 @@ from moviepy.video.VideoClip import VideoClip


 class VideoFileClip(VideoClip):
+
     """

     A video clip originating from a movie file. For instance: ::
@@ -73,33 +75,60 @@ class VideoFileClip(VideoClip):

     """

-    def __init__(self, filename, has_mask=False, audio=True,
-        audio_buffersize=200000, target_resolution=None, resize_algorithm=
-        'bicubic', audio_fps=44100, audio_nbytes=2, verbose=False,
-        fps_source='tbr'):
+    def __init__(self, filename, has_mask=False,
+                 audio=True, audio_buffersize=200000,
+                 target_resolution=None, resize_algorithm='bicubic',
+                 audio_fps=44100, audio_nbytes=2, verbose=False,
+                 fps_source='tbr'):
+
         VideoClip.__init__(self)
-        pix_fmt = 'rgba' if has_mask else 'rgb24'
+
+        # Make a reader
+        pix_fmt = "rgba" if has_mask else "rgb24"
         self.reader = FFMPEG_VideoReader(filename, pix_fmt=pix_fmt,
-            target_resolution=target_resolution, resize_algo=
-            resize_algorithm, fps_source=fps_source)
+                                         target_resolution=target_resolution,
+                                         resize_algo=resize_algorithm,
+                                         fps_source=fps_source)
+
+        # Make some of the reader's attributes accessible from the clip
         self.duration = self.reader.duration
         self.end = self.reader.duration
+
         self.fps = self.reader.fps
         self.size = self.reader.size
         self.rotation = self.reader.rotation
+
         self.filename = self.reader.filename
+
         if has_mask:
-            self.make_frame = lambda t: self.reader.get_frame(t)[:, :, :3]
-            mask_mf = lambda t: self.reader.get_frame(t)[:, :, 3] / 255.0
-            self.mask = VideoClip(ismask=True, make_frame=mask_mf
-                ).set_duration(self.duration)
+
+            self.make_frame = lambda t: self.reader.get_frame(t)[:,:,:3]
+            mask_mf = lambda t: self.reader.get_frame(t)[:,:,3]/255.0
+            self.mask = (VideoClip(ismask=True, make_frame=mask_mf)
+                         .set_duration(self.duration))
             self.mask.fps = self.fps
+
         else:
+
             self.make_frame = lambda t: self.reader.get_frame(t)
+
+        # Make a reader for the audio, if any.
         if audio and self.reader.infos['audio_found']:
-            self.audio = AudioFileClip(filename, buffersize=
-                audio_buffersize, fps=audio_fps, nbytes=audio_nbytes)
+
+            self.audio = AudioFileClip(filename,
+                                       buffersize=audio_buffersize,
+                                       fps=audio_fps,
+                                       nbytes=audio_nbytes)

     def close(self):
         """ Close the internal reader. """
-        pass
+        if self.reader:
+            self.reader.close()
+            self.reader = None
+
+        try:
+            if self.audio:
+                self.audio.close()
+                self.audio = None
+        except AttributeError:
+            pass
diff --git a/moviepy/video/io/bindings.py b/moviepy/video/io/bindings.py
index 21d54a8..4b206f7 100644
--- a/moviepy/video/io/bindings.py
+++ b/moviepy/video/io/bindings.py
@@ -2,15 +2,31 @@
 This module implements all the functions to communicate with other Python
 modules (PIL, matplotlib, mayavi, etc.)
 """
+
 import numpy as np


 def PIL_to_npimage(im):
     """ Transforms a PIL/Pillow image into a numpy RGB(A) image.
         Actually all this do is returning numpy.array(im)."""
-    pass
+    return np.array(im)
+    #w,h = im.size
+    #d = (4 if im.mode=="RGBA" else 3)
+    #return +np.frombuffer(im.tobytes(), dtype='uint8').reshape((h,w,d))


 def mplfig_to_npimage(fig):
     """ Converts a matplotlib figure to a RGB frame after updating the canvas"""
-    pass
+    #  only the Agg backend now supports the tostring_rgb function
+    from matplotlib.backends.backend_agg import FigureCanvasAgg
+    canvas = FigureCanvasAgg(fig)
+    canvas.draw() # update/draw the elements
+
+    # get the width and the height to resize the matrix
+    l,b,w,h = canvas.figure.bbox.bounds
+    w, h = int(w), int(h)
+
+    #  exports the canvas to a string buffer and then to a numpy nd.array
+    buf = canvas.tostring_rgb()
+    image= np.frombuffer(buf, dtype=np.uint8)
+    return image.reshape(h,w,3)
diff --git a/moviepy/video/io/downloader.py b/moviepy/video/io/downloader.py
index 82a3b18..5b579de 100644
--- a/moviepy/video/io/downloader.py
+++ b/moviepy/video/io/downloader.py
@@ -1,8 +1,11 @@
 """
 Utilities to get a file from the internet
 """
+
 import os
+
 import requests
+
 from moviepy.tools import subprocess_call


@@ -12,4 +15,20 @@ def download_webfile(url, filename, overwrite=False):
     using youtube-dl (install youtube-dl first !).
     If the filename already exists and overwrite=False, nothing will happen.
     """
-    pass
+    if os.path.exists(filename) and not overwrite:
+        return
+
+    if '.' in url:
+        r = requests.get(url, stream=True)
+        with open(filename, 'wb') as fd:
+            for chunk in r.iter_content(chunk_size=128):
+                fd.write(chunk)
+
+    else:
+        try:
+            subprocess_call(['youtube-dl', url, '-o', filename])
+        except OSError as e:
+            raise OSError(
+                e.message + '\n A possible reason is that youtube-dl'
+                ' is not installed on your computer. Install it with '
+                ' "pip install youtube_dl"')
diff --git a/moviepy/video/io/ffmpeg_reader.py b/moviepy/video/io/ffmpeg_reader.py
index fd9fccf..7ef5b2d 100644
--- a/moviepy/video/io/ffmpeg_reader.py
+++ b/moviepy/video/io/ffmpeg_reader.py
@@ -2,64 +2,153 @@
 This module implements all the functions to read a video or a picture
 using ffmpeg. It is quite ugly, as there are many pitfalls to avoid
 """
+
 from __future__ import division
+
 import logging
 import os
 import re
 import subprocess as sp
 import warnings
+
 import numpy as np
+
 from moviepy.compat import DEVNULL, PY3
-from moviepy.config import get_setting
+from moviepy.config import get_setting  # ffmpeg, ffmpeg.exe, etc...
 from moviepy.tools import cvsecs
+
 logging.captureWarnings(True)


+
+
+
 class FFMPEG_VideoReader:

-    def __init__(self, filename, print_infos=False, bufsize=None, pix_fmt=
-        'rgb24', check_duration=True, target_resolution=None, resize_algo=
-        'bicubic', fps_source='tbr'):
+    def __init__(self, filename, print_infos=False, bufsize = None,
+                 pix_fmt="rgb24", check_duration=True,
+                 target_resolution=None, resize_algo='bicubic',
+                 fps_source='tbr'):
+
         self.filename = filename
         self.proc = None
         infos = ffmpeg_parse_infos(filename, print_infos, check_duration,
-            fps_source)
+                                   fps_source)
         self.fps = infos['video_fps']
         self.size = infos['video_size']
         self.rotation = infos['video_rotation']
+
         if target_resolution:
+            # revert the order, as ffmpeg used (width, height)
             target_resolution = target_resolution[1], target_resolution[0]
+
             if None in target_resolution:
                 ratio = 1
                 for idx, target in enumerate(target_resolution):
                     if target:
                         ratio = target / self.size[idx]
-                self.size = int(self.size[0] * ratio), int(self.size[1] * ratio
-                    )
+                self.size = (int(self.size[0] * ratio), int(self.size[1] * ratio))
             else:
                 self.size = target_resolution
         self.resize_algo = resize_algo
+
         self.duration = infos['video_duration']
         self.ffmpeg_duration = infos['duration']
         self.nframes = infos['video_nframes']
+
         self.infos = infos
+
         self.pix_fmt = pix_fmt
         self.depth = 4 if pix_fmt == 'rgba' else 3
+
         if bufsize is None:
             w, h = self.size
             bufsize = self.depth * w * h + 100
-        self.bufsize = bufsize
+
+        self.bufsize= bufsize
         self.initialize()
+
+
         self.pos = 1
         self.lastread = self.read_frame()

+
     def initialize(self, starttime=0):
         """Opens the file, creates the pipe. """
-        pass
+
+        self.close() # if any
+
+        if starttime != 0 :
+            offset = min(1, starttime)
+            i_arg = ['-ss', "%.06f" % (starttime - offset),
+                     '-i', self.filename,
+                     '-ss', "%.06f" % offset]
+        else:
+            i_arg = [ '-i', self.filename]
+
+        cmd = ([get_setting("FFMPEG_BINARY")] + i_arg +
+               ['-loglevel', 'error',
+                '-f', 'image2pipe',
+                '-vf', 'scale=%d:%d' % tuple(self.size),
+                '-sws_flags', self.resize_algo,
+                "-pix_fmt", self.pix_fmt,
+                '-vcodec', 'rawvideo', '-'])
+        popen_params = {"bufsize": self.bufsize,
+                        "stdout": sp.PIPE,
+                        "stderr": sp.PIPE,
+                        "stdin": DEVNULL}
+
+        if os.name == "nt":
+            popen_params["creationflags"] = 0x08000000
+
+        self.proc = sp.Popen(cmd, **popen_params)
+

     def skip_frames(self, n=1):
         """Reads and throws away n frames """
-        pass
+        w, h = self.size
+        for i in range(n):
+            self.proc.stdout.read(self.depth*w*h)
+            #self.proc.stdout.flush()
+        self.pos += n
+
+
+    def read_frame(self):
+        w, h = self.size
+        nbytes= self.depth*w*h
+
+        s = self.proc.stdout.read(nbytes)
+        if len(s) != nbytes:
+
+            warnings.warn("Warning: in file %s, "%(self.filename)+
+                   "%d bytes wanted but %d bytes read,"%(nbytes, len(s))+
+                   "at frame %d/%d, at time %.02f/%.02f sec. "%(
+                    self.pos,self.nframes,
+                    1.0*self.pos/self.fps,
+                    self.duration)+
+                   "Using the last valid frame instead.",
+                   UserWarning)
+
+            if not hasattr(self, 'lastread'):
+                raise IOError(("MoviePy error: failed to read the first frame of "
+                               "video file %s. That might mean that the file is "
+                               "corrupted. That may also mean that you are using "
+                               "a deprecated version of FFMPEG. On Ubuntu/Debian "
+                               "for instance the version in the repos is deprecated. "
+                               "Please update to a recent version from the website.")%(
+                                self.filename))
+
+            result = self.lastread
+
+        else:
+            if hasattr(np, 'frombuffer'):
+                result = np.frombuffer(s, dtype='uint8')
+            else:
+                result = np.fromstring(s, dtype='uint8')
+            result.shape =(h, w, len(s)//(w*h)) # reshape((h, w, len(s)//(w*h)))
+            self.lastread = result
+
+        return result

     def get_frame(self, t):
         """ Read a file video frame at time t.
@@ -69,7 +158,42 @@ class FFMPEG_VideoReader:
         This function tries to avoid fetching arbitrary frames
         whenever possible, by moving between adjacent frames.
         """
-        pass
+
+        # these definitely need to be rechecked sometime. Seems to work.
+
+        # I use that horrible '+0.00001' hack because sometimes due to numerical
+        # imprecisions a 3.0 can become a 2.99999999... which makes the int()
+        # go to the previous integer. This makes the fetching more robust in the
+        # case where you get the nth frame by writing get_frame(n/fps).
+
+        pos = int(self.fps*t + 0.00001)+1
+
+        # Initialize proc if it is not open
+        if not self.proc:
+            self.initialize(t)
+            self.pos = pos
+            self.lastread = self.read_frame()
+
+        if pos == self.pos:
+            return self.lastread
+        elif (pos < self.pos) or (pos > self.pos + 100):
+            self.initialize(t)
+            self.pos = pos
+        else:
+            self.skip_frames(pos-self.pos-1)
+        result = self.read_frame()
+        self.pos = pos
+        return result
+
+    def close(self):
+        if self.proc:
+            self.proc.terminate()
+            self.proc.stdout.close()
+            self.proc.stderr.close()
+            self.proc.wait()
+            self.proc = None
+        if hasattr(self, 'lastread'):
+            del self.lastread

     def __del__(self):
         self.close()
@@ -95,11 +219,15 @@ def ffmpeg_read_image(filename, with_mask=True):
       this layer as the mask of the returned ImageClip

     """
-    pass
+    pix_fmt = 'rgba' if with_mask else "rgb24" 
+    reader = FFMPEG_VideoReader(filename, pix_fmt=pix_fmt, check_duration=False)
+    im = reader.lastread
+    del reader
+    return im


 def ffmpeg_parse_infos(filename, print_infos=False, check_duration=True,
-    fps_source='tbr'):
+                       fps_source='tbr'):
     """Get file infos using ffmpeg.

     Returns a dictionnary with the fields:
@@ -110,4 +238,156 @@ def ffmpeg_parse_infos(filename, print_infos=False, check_duration=True,
     fetching the uncomplete frames at the end, which raises an error.

     """
-    pass
+
+
+    # open the file in a pipe, provoke an error, read output
+    is_GIF = filename.endswith('.gif')
+    cmd = [get_setting("FFMPEG_BINARY"), "-i", filename]
+    if is_GIF:
+        cmd += ["-f", "null", "/dev/null"]
+
+    popen_params = {"bufsize": 10**5,
+                    "stdout": sp.PIPE,
+                    "stderr": sp.PIPE,
+                    "stdin": DEVNULL}
+
+    if os.name == "nt":
+        popen_params["creationflags"] = 0x08000000
+
+    proc = sp.Popen(cmd, **popen_params)
+    (output, error) = proc.communicate()
+    infos = error.decode('utf8')
+
+    del proc
+
+    if print_infos:
+        # print the whole info text returned by FFMPEG
+        print(infos)
+
+
+    lines = infos.splitlines()
+    if "No such file or directory" in lines[-1]:
+        raise IOError(("MoviePy error: the file %s could not be found!\n"
+                      "Please check that you entered the correct "
+                      "path.")%filename)
+
+    result = dict()
+
+
+    # get duration (in seconds)
+    result['duration'] = None
+
+    if check_duration:
+        try:
+            keyword = ('frame=' if is_GIF else 'Duration: ')
+            # for large GIFS the "full" duration is presented as the last element in the list.
+            index = -1 if is_GIF else 0
+            line = [l for l in lines if keyword in l][index]
+            match = re.findall("([0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9])", line)[0]
+            result['duration'] = cvsecs(match)
+        except:
+            raise IOError(("MoviePy error: failed to read the duration of file %s.\n"
+                           "Here are the file infos returned by ffmpeg:\n\n%s")%(
+                              filename, infos))
+
+    # get the output line that speaks about video
+    lines_video = [l for l in lines if ' Video: ' in l and re.search('\d+x\d+', l)]
+
+    result['video_found'] = ( lines_video != [] )
+
+    if result['video_found']:
+        try:
+            line = lines_video[0]
+
+            # get the size, of the form 460x320 (w x h)
+            match = re.search(" [0-9]*x[0-9]*(,| )", line)
+            s = list(map(int, line[match.start():match.end()-1].split('x')))
+            result['video_size'] = s
+        except:
+            raise IOError(("MoviePy error: failed to read video dimensions in file %s.\n"
+                           "Here are the file infos returned by ffmpeg:\n\n%s")%(
+                              filename, infos))
+
+        # Get the frame rate. Sometimes it's 'tbr', sometimes 'fps', sometimes
+        # tbc, and sometimes tbc/2...
+        # Current policy: Trust tbr first, then fps unless fps_source is
+        # specified as 'fps' in which case try fps then tbr
+
+        # If result is near from x*1000/1001 where x is 23,24,25,50,
+        # replace by x*1000/1001 (very common case for the fps).
+
+        def get_tbr():
+            match = re.search("( [0-9]*.| )[0-9]* tbr", line)
+
+            # Sometimes comes as e.g. 12k. We need to replace that with 12000.
+            s_tbr = line[match.start():match.end()].split(' ')[1]
+            if "k" in s_tbr:
+                tbr = float(s_tbr.replace("k", "")) * 1000
+            else:
+                tbr = float(s_tbr)
+            return tbr
+
+        def get_fps():
+            match = re.search("( [0-9]*.| )[0-9]* fps", line)
+            fps = float(line[match.start():match.end()].split(' ')[1])
+            return fps
+
+        if fps_source == 'tbr':
+            try:
+                result['video_fps'] = get_tbr()
+            except:
+                result['video_fps'] = get_fps()
+
+        elif fps_source == 'fps':
+            try:
+                result['video_fps'] = get_fps()
+            except:
+                result['video_fps'] = get_tbr()
+
+        # It is known that a fps of 24 is often written as 24000/1001
+        # but then ffmpeg nicely rounds it to 23.98, which we hate.
+        coef = 1000.0/1001.0
+        fps = result['video_fps']
+        for x in [23,24,25,30,50]:
+            if (fps!=x) and abs(fps - x*coef) < .01:
+                result['video_fps'] = x*coef
+
+        if check_duration:
+            result['video_nframes'] = int(result['duration']*result['video_fps'])+1
+            result['video_duration'] = result['duration']
+        else:
+            result['video_nframes'] = 1
+            result['video_duration'] = None
+        # We could have also recomputed the duration from the number
+        # of frames, as follows:
+        # >>> result['video_duration'] = result['video_nframes'] / result['video_fps']
+
+        # get the video rotation info.
+        try:
+            rotation_lines = [l for l in lines if 'rotate          :' in l and re.search('\d+$', l)]
+            if len(rotation_lines):
+                rotation_line = rotation_lines[0]
+                match = re.search('\d+$', rotation_line)
+                result['video_rotation'] = int(rotation_line[match.start() : match.end()])
+            else:
+                result['video_rotation'] = 0
+        except:
+            raise IOError(("MoviePy error: failed to read video rotation in file %s.\n"
+                           "Here are the file infos returned by ffmpeg:\n\n%s")%(
+                              filename, infos))
+
+
+    lines_audio = [l for l in lines if ' Audio: ' in l]
+
+    result['audio_found'] = lines_audio != []
+
+    if result['audio_found']:
+        line = lines_audio[0]
+        try:
+            match = re.search(" [0-9]* Hz", line)
+            hz_string = line[match.start()+1:match.end()-3]  # Removes the 'hz' from the end
+            result['audio_fps'] = int(hz_string)
+        except:
+            result['audio_fps'] = 'unknown'
+
+    return result
diff --git a/moviepy/video/io/ffmpeg_tools.py b/moviepy/video/io/ffmpeg_tools.py
index cf73b19..ef66421 100644
--- a/moviepy/video/io/ffmpeg_tools.py
+++ b/moviepy/video/io/ffmpeg_tools.py
@@ -1,7 +1,9 @@
 """ Misc. bindings to ffmpeg and ImageMagick."""
+
 import os
 import subprocess as sp
 import sys
+
 from moviepy.config import get_setting
 from moviepy.tools import subprocess_call

@@ -11,28 +13,56 @@ def ffmpeg_movie_from_frames(filename, folder, fps, digits=6, bitrate='v'):
     Writes a movie out of the frames (picture files) in a folder.
     Almost deprecated.
     """
-    pass
+    s = "%" + "%02d" % digits + "d.png"
+    cmd = [get_setting("FFMPEG_BINARY"), "-y", "-f","image2",
+             "-r", "%d"%fps,
+             "-i", os.path.join(folder,folder) + '/' + s,
+             "-b", "%dk"%bitrate,
+             "-r", "%d"%fps,
+             filename]
+    
+    subprocess_call(cmd)


 def ffmpeg_extract_subclip(filename, t1, t2, targetname=None):
     """ Makes a new video file playing video file ``filename`` between
         the times ``t1`` and ``t2``. """
-    pass
+    name, ext = os.path.splitext(filename)
+    if not targetname:
+        T1, T2 = [int(1000*t) for t in [t1, t2]]
+        targetname = "%sSUB%d_%d.%s" % (name, T1, T2, ext)
+    
+    cmd = [get_setting("FFMPEG_BINARY"),"-y",
+           "-ss", "%0.2f"%t1,
+           "-i", filename,
+           "-t", "%0.2f"%(t2-t1),
+           "-map", "0", "-vcodec", "copy", "-acodec", "copy", targetname]
+    
+    subprocess_call(cmd)


-def ffmpeg_merge_video_audio(video, audio, output, vcodec='copy', acodec=
-    'copy', ffmpeg_output=False, logger='bar'):
+def ffmpeg_merge_video_audio(video,audio,output, vcodec='copy',
+                             acodec='copy', ffmpeg_output=False,
+                             logger = 'bar'):
     """ merges video file ``video`` and audio file ``audio`` into one
         movie file ``output``. """
-    pass
+    cmd = [get_setting("FFMPEG_BINARY"), "-y", "-i", audio,"-i", video,
+             "-vcodec", vcodec, "-acodec", acodec, output]
+             
+    subprocess_call(cmd, logger = logger)
+    

-
-def ffmpeg_extract_audio(inputfile, output, bitrate=3000, fps=44100):
+def ffmpeg_extract_audio(inputfile,output,bitrate=3000,fps=44100):
     """ extract the sound from a video file and save it in ``output`` """
-    pass
-
+    cmd = [get_setting("FFMPEG_BINARY"), "-y", "-i", inputfile, "-ab", "%dk"%bitrate,
+         "-ar", "%d"%fps, output]
+    subprocess_call(cmd)
+    

-def ffmpeg_resize(video, output, size):
+def ffmpeg_resize(video,output,size):
     """ resizes ``video`` to new size ``size`` and write the result
         in file ``output``. """
-    pass
+    cmd= [get_setting("FFMPEG_BINARY"), "-i", video, "-vf", "scale=%d:%d"%(size[0], size[1]),
+             output]
+             
+    subprocess_call(cmd)
diff --git a/moviepy/video/io/ffmpeg_writer.py b/moviepy/video/io/ffmpeg_writer.py
index 5d6bb8b..7fdd1dd 100644
--- a/moviepy/video/io/ffmpeg_writer.py
+++ b/moviepy/video/io/ffmpeg_writer.py
@@ -2,10 +2,13 @@
 On the long term this will implement several methods to make videos
 out of VideoClips
 """
+
 import os
 import subprocess as sp
+
 import numpy as np
 from proglog import proglog
+
 from moviepy.compat import DEVNULL, PY3
 from moviepy.config import get_setting

@@ -62,39 +65,130 @@ class FFMPEG_VideoWriter:

     """

-    def __init__(self, filename, size, fps, codec='libx264', audiofile=None,
-        preset='medium', bitrate=None, withmask=False, logfile=None,
-        threads=None, ffmpeg_params=None):
+    def __init__(self, filename, size, fps, codec="libx264", audiofile=None,
+                 preset="medium", bitrate=None, withmask=False,
+                 logfile=None, threads=None, ffmpeg_params=None):
+
         if logfile is None:
             logfile = sp.PIPE
+
         self.filename = filename
         self.codec = codec
-        self.ext = self.filename.split('.')[-1]
-        cmd = [get_setting('FFMPEG_BINARY'), '-y', '-loglevel', 'error' if 
-            logfile == sp.PIPE else 'info', '-f', 'rawvideo', '-vcodec',
-            'rawvideo', '-s', '%dx%d' % (size[0], size[1]), '-pix_fmt', 
-            'rgba' if withmask else 'rgb24', '-r', '%.02f' % fps, '-an',
-            '-i', '-']
+        self.ext = self.filename.split(".")[-1]
+
+        # order is important
+        cmd = [
+            get_setting("FFMPEG_BINARY"),
+            '-y',
+            '-loglevel', 'error' if logfile == sp.PIPE else 'info',
+            '-f', 'rawvideo',
+            '-vcodec', 'rawvideo',
+            '-s', '%dx%d' % (size[0], size[1]),
+            '-pix_fmt', 'rgba' if withmask else 'rgb24',
+            '-r', '%.02f' % fps,
+            '-an', '-i', '-'
+        ]
         if audiofile is not None:
-            cmd.extend(['-i', audiofile, '-acodec', 'copy'])
-        cmd.extend(['-vcodec', codec, '-preset', preset])
+            cmd.extend([
+                '-i', audiofile,
+                '-acodec', 'copy'
+            ])
+        cmd.extend([
+            '-vcodec', codec,
+            '-preset', preset,
+        ])
         if ffmpeg_params is not None:
             cmd.extend(ffmpeg_params)
         if bitrate is not None:
-            cmd.extend(['-b', bitrate])
+            cmd.extend([
+                '-b', bitrate
+            ])
+
         if threads is not None:
-            cmd.extend(['-threads', str(threads)])
-        if codec == 'libx264' and size[0] % 2 == 0 and size[1] % 2 == 0:
-            cmd.extend(['-pix_fmt', 'yuv420p'])
-        cmd.extend([filename])
-        popen_params = {'stdout': DEVNULL, 'stderr': logfile, 'stdin': sp.PIPE}
-        if os.name == 'nt':
-            popen_params['creationflags'] = 134217728
+            cmd.extend(["-threads", str(threads)])
+
+        if ((codec == 'libx264') and
+                (size[0] % 2 == 0) and
+                (size[1] % 2 == 0)):
+            cmd.extend([
+                '-pix_fmt', 'yuv420p'
+            ])
+        cmd.extend([
+            filename
+        ])
+
+        popen_params = {"stdout": DEVNULL,
+                        "stderr": logfile,
+                        "stdin": sp.PIPE}
+
+        # This was added so that no extra unwanted window opens on windows
+        # when the child process is created
+        if os.name == "nt":
+            popen_params["creationflags"] = 0x08000000  # CREATE_NO_WINDOW
+
         self.proc = sp.Popen(cmd, **popen_params)

+
     def write_frame(self, img_array):
         """ Writes one frame in the file."""
-        pass
+        try:
+            if PY3:
+               self.proc.stdin.write(img_array.tobytes())
+            else:
+               self.proc.stdin.write(img_array.tostring())
+        except IOError as err:
+            _, ffmpeg_error = self.proc.communicate()
+            error = (str(err) + ("\n\nMoviePy error: FFMPEG encountered "
+                                 "the following error while writing file %s:"
+                                 "\n\n %s" % (self.filename, str(ffmpeg_error))))
+
+            if b"Unknown encoder" in ffmpeg_error:
+
+                error = error+("\n\nThe video export "
+                  "failed because FFMPEG didn't find the specified "
+                  "codec for video encoding (%s). Please install "
+                  "this codec or change the codec when calling "
+                  "write_videofile. For instance:\n"
+                  "  >>> clip.write_videofile('myvid.webm', codec='libvpx')")%(self.codec)
+
+            elif b"incorrect codec parameters ?" in ffmpeg_error:
+
+                 error = error+("\n\nThe video export "
+                  "failed, possibly because the codec specified for "
+                  "the video (%s) is not compatible with the given "
+                  "extension (%s). Please specify a valid 'codec' "
+                  "argument in write_videofile. This would be 'libx264' "
+                  "or 'mpeg4' for mp4, 'libtheora' for ogv, 'libvpx for webm. "
+                  "Another possible reason is that the audio codec was not "
+                  "compatible with the video codec. For instance the video "
+                  "extensions 'ogv' and 'webm' only allow 'libvorbis' (default) as a"
+                  "video codec."
+                  )%(self.codec, self.ext)
+
+            elif  b"encoder setup failed" in ffmpeg_error:
+
+                error = error+("\n\nThe video export "
+                  "failed, possibly because the bitrate you specified "
+                  "was too high or too low for the video codec.")
+
+            elif b"Invalid encoder type" in ffmpeg_error:
+
+                error = error + ("\n\nThe video export failed because the codec "
+                  "or file extension you provided is not a video")
+
+
+            raise IOError(error)
+
+    def close(self):
+        if self.proc:
+            self.proc.stdin.close()
+            if self.proc.stderr is not None:
+                self.proc.stderr.close()
+            self.proc.wait()
+
+        self.proc = None
+
+    # Support the Context Manager protocol, to ensure that resources are cleaned up.

     def __enter__(self):
         return self
@@ -102,17 +196,74 @@ class FFMPEG_VideoWriter:
     def __exit__(self, exc_type, exc_value, traceback):
         self.close()

-
-def ffmpeg_write_video(clip, filename, fps, codec='libx264', bitrate=None,
-    preset='medium', withmask=False, write_logfile=False, audiofile=None,
-    verbose=True, threads=None, ffmpeg_params=None, logger='bar'):
+def ffmpeg_write_video(clip, filename, fps, codec="libx264", bitrate=None,
+                       preset="medium", withmask=False, write_logfile=False,
+                       audiofile=None, verbose=True, threads=None, ffmpeg_params=None,
+                       logger='bar'):
     """ Write the clip to a videofile. See VideoClip.write_videofile for details
     on the parameters.
     """
-    pass
+    logger = proglog.default_bar_logger(logger)
+
+    if write_logfile:
+        logfile = open(filename + ".log", 'w+')
+    else:
+        logfile = None
+    logger(message='Moviepy - Writing video %s\n' % filename)
+    with FFMPEG_VideoWriter(filename, clip.size, fps, codec = codec,
+                                preset=preset, bitrate=bitrate, logfile=logfile,
+                                audiofile=audiofile, threads=threads,
+                                ffmpeg_params=ffmpeg_params) as writer:
+
+        nframes = int(clip.duration*fps)
+
+        for t,frame in clip.iter_frames(logger=logger, with_times=True,
+                                        fps=fps, dtype="uint8"):
+            if withmask:
+                mask = (255*clip.mask.get_frame(t))
+                if mask.dtype != "uint8":
+                    mask = mask.astype("uint8")
+                frame = np.dstack([frame,mask])
+
+            writer.write_frame(frame)
+
+    if write_logfile:
+        logfile.close()
+    logger(message='Moviepy - Done !')


 def ffmpeg_write_image(filename, image, logfile=False):
     """ Writes an image (HxWx3 or HxWx4 numpy array) to a file, using
         ffmpeg. """
-    pass
+
+    if image.dtype != 'uint8':
+          image = image.astype("uint8")
+
+    cmd = [ get_setting("FFMPEG_BINARY"), '-y',
+           '-s', "%dx%d"%(image.shape[:2][::-1]),
+           "-f", 'rawvideo',
+           '-pix_fmt', "rgba" if (image.shape[2] == 4) else "rgb24",
+           '-i','-', filename]
+
+    if logfile:
+        log_file = open(filename + ".log", 'w+')
+    else:
+        log_file = sp.PIPE
+
+    popen_params = {"stdout": DEVNULL,
+                    "stderr": log_file,
+                    "stdin": sp.PIPE}
+
+    if os.name == "nt":
+        popen_params["creationflags"] = 0x08000000
+
+    proc = sp.Popen(cmd, **popen_params)
+    out, err = proc.communicate(image.tostring())
+
+    if proc.returncode:
+        err = "\n".join(["[MoviePy] Running : %s\n" % cmd,
+                         "WARNING: this command returned an error:",
+                         err.decode('utf8')])
+        raise IOError(err)
+
+    del proc
diff --git a/moviepy/video/io/gif_writers.py b/moviepy/video/io/gif_writers.py
index 5b4de02..b17d0e6 100644
--- a/moviepy/video/io/gif_writers.py
+++ b/moviepy/video/io/gif_writers.py
@@ -1,11 +1,14 @@
 import os
 import subprocess as sp
+
 import numpy as np
 import proglog
+
 from moviepy.compat import DEVNULL
 from moviepy.config import get_setting
 from moviepy.decorators import requires_duration, use_clip_fps_by_default
 from moviepy.tools import subprocess_call
+
 try:
     import imageio
     IMAGEIO_FOUND = True
@@ -15,9 +18,9 @@ except ImportError:

 @requires_duration
 @use_clip_fps_by_default
-def write_gif_with_tempfiles(clip, filename, fps=None, program=
-    'ImageMagick', opt='OptimizeTransparency', fuzz=1, verbose=True, loop=0,
-    dispose=True, colors=None, logger='bar'):
+def write_gif_with_tempfiles(clip, filename, fps=None, program= 'ImageMagick',
+       opt="OptimizeTransparency", fuzz=1, verbose=True,
+       loop=0, dispose=True, colors=None, logger='bar'):
     """ Write the VideoClip to a GIF file.


@@ -27,14 +30,72 @@ def write_gif_with_tempfiles(clip, filename, fps=None, program=
     them in the RAM. Useful on computers with little RAM.

     """
-    pass
+    logger = proglog.default_bar_logger(logger)
+    fileName, ext = os.path.splitext(filename)
+    tt = np.arange(0,clip.duration, 1.0/fps)
+
+    tempfiles = []
+    
+    logger(message='MoviePy - Building file %s\n' % filename)
+    logger(message='MoviePy - - Generating GIF frames')
+
+    
+    for i, t in logger.iter_bar(t=list(enumerate(tt))):
+
+        name = "%s_GIFTEMP%04d.png"%(fileName, i+1)
+        tempfiles.append(name)
+        clip.save_frame(name, t, withmask=True)
+
+    delay = int(100.0/fps)
+
+    if program == "ImageMagick":
+        logger(message='MoviePy - - Optimizing GIF with ImageMagick...')
+        cmd = [get_setting("IMAGEMAGICK_BINARY"),
+              '-delay' , '%d'%delay,
+              "-dispose" ,"%d"%(2 if dispose else 1),
+              "-loop" , "%d"%loop,
+              "%s_GIFTEMP*.png"%fileName,
+              "-coalesce",
+              "-fuzz", "%02d"%fuzz + "%",
+              "-layers", "%s"%opt,
+              ]+(["-colors", "%d"%colors] if colors is not None else [])+[
+              filename]
+
+    elif program == "ffmpeg":
+
+        cmd = [get_setting("FFMPEG_BINARY"), '-y',
+               '-f', 'image2', '-r',str(fps),
+               '-i', fileName+'_GIFTEMP%04d.png',
+               '-r',str(fps),
+               filename]
+
+    try:
+        subprocess_call(cmd, logger=logger)
+        logger(message='MoviePy - GIF ready: %s.' % filename)
+
+    except (IOError,OSError) as err:
+
+        error = ("MoviePy Error: creation of %s failed because "
+          "of the following error:\n\n%s.\n\n."%(filename, str(err)))
+
+        if program == "ImageMagick":
+            error = error + ("This error can be due to the fact that "
+                "ImageMagick is not installed on your computer, or "
+                "(for Windows users) that you didn't specify the "
+                "path to the ImageMagick binary in file config_defaults.py." )
+
+        raise IOError(error)
+
+    for f in tempfiles:
+        os.remove(f)
+


 @requires_duration
 @use_clip_fps_by_default
-def write_gif(clip, filename, fps=None, program='ImageMagick', opt=
-    'OptimizeTransparency', fuzz=1, verbose=True, withmask=True, loop=0,
-    dispose=True, colors=None, logger='bar'):
+def write_gif(clip, filename, fps=None, program= 'ImageMagick',
+           opt="OptimizeTransparency", fuzz=1, verbose=True, withmask=True,
+           loop=0, dispose=True, colors=None, logger='bar'):
     """ Write the VideoClip to a GIF file, without temporary files.

     Converts a VideoClip into an animated GIF using ImageMagick
@@ -77,11 +138,117 @@ def write_gif(clip, filename, fps=None, program='ImageMagick', opt=
         >>> myClip.speedx(0.5).write_gif('myClip.gif')

     """
-    pass

+    #
+    # We use processes chained with pipes.
+    #
+    # if program == 'ffmpeg'
+    # frames --ffmpeg--> gif
+    #
+    # if program == 'ImageMagick' and optimize == (None, False)
+    # frames --ffmpeg--> bmp frames --ImageMagick--> gif
+    #
+    #
+    # if program == 'ImageMagick' and optimize != (None, False)
+    # frames -ffmpeg-> bmp frames -ImagMag-> gif -ImagMag-> better gif
+    #
+
+    delay= 100.0/fps
+    logger = proglog.default_bar_logger(logger)
+    if clip.mask is None:
+        withmask = False
+
+    cmd1 = [get_setting("FFMPEG_BINARY"), '-y', '-loglevel', 'error',
+            '-f', 'rawvideo',
+            '-vcodec','rawvideo', '-r', "%.02f"%fps,
+            '-s', "%dx%d"%(clip.w, clip.h),
+            '-pix_fmt', ('rgba' if withmask else 'rgb24'),
+            '-i', '-']
+
+    popen_params = {"stdout": DEVNULL,
+                    "stderr": DEVNULL,
+                    "stdin": DEVNULL}
+
+    if os.name == "nt":
+        popen_params["creationflags"] = 0x08000000
+
+    if program == "ffmpeg":
+        popen_params["stdin"] = sp.PIPE
+        popen_params["stdout"] = DEVNULL
+
+        proc1 = sp.Popen(cmd1+[ '-pix_fmt', ('rgba' if withmask else 'rgb24'),
+                                '-r', "%.02f"%fps, filename], **popen_params)
+    else:

-def write_gif_with_image_io(clip, filename, fps=None, opt=0, loop=0, colors
-    =None, verbose=True, logger='bar'):
+        popen_params["stdin"] = sp.PIPE
+        popen_params["stdout"] = sp.PIPE
+
+        proc1 = sp.Popen(cmd1+ ['-f', 'image2pipe', '-vcodec', 'bmp', '-'],
+                         **popen_params)
+
+    if program == 'ImageMagick':
+
+        cmd2 = [get_setting("IMAGEMAGICK_BINARY"), '-delay', "%.02f"%(delay),
+                "-dispose" ,"%d"%(2 if dispose else 1),
+                '-loop', '%d'%loop, '-', '-coalesce']
+
+        if (opt in [False, None]):
+            popen_params["stdin"] = proc1.stdout
+            popen_params["stdout"] = DEVNULL
+            proc2 = sp.Popen(cmd2+[filename], **popen_params)
+
+        else:
+            popen_params["stdin"] = proc1.stdout
+            popen_params["stdout"] = sp.PIPE
+            proc2 = sp.Popen(cmd2+['gif:-'], **popen_params)
+
+        if opt:
+
+            cmd3 = [get_setting("IMAGEMAGICK_BINARY"), '-',
+                    '-fuzz', '%d'%fuzz+'%', '-layers', opt
+                   ]+(["-colors", "%d"%colors] if colors is not None else [])+[
+                   filename]
+
+            popen_params["stdin"] = proc2.stdout
+            popen_params["stdout"] = DEVNULL
+            proc3 = sp.Popen(cmd3, **popen_params)
+
+    # We send all the frames to the first process
+    logger(message='MoviePy - Building file  %s' % filename)
+    logger(message='MoviePy - - Generating GIF frames.')
+    try:
+        for t,frame in clip.iter_frames(fps=fps, logger=logger,
+                                        with_times=True,  dtype="uint8"):
+            if withmask:
+                mask = 255 * clip.mask.get_frame(t)
+                frame = np.dstack([frame, mask]).astype('uint8')
+            proc1.stdin.write(frame.tostring())
+
+    except IOError as err:
+
+        error = ("[MoviePy] Error: creation of %s failed because "
+          "of the following error:\n\n%s.\n\n."%(filename, str(err)))
+
+        if program == "ImageMagick":
+            error = error + ("This can be due to the fact that "
+                "ImageMagick is not installed on your computer, or "
+                "(for Windows users) that you didn't specify the "
+                "path to the ImageMagick binary in file config_defaults.py." )
+
+        raise IOError(error)
+    if program == 'ImageMagick':
+        logger(message='MoviePy - - Optimizing GIF with ImageMagick.')
+    proc1.stdin.close()
+    proc1.wait()
+    if program == 'ImageMagick':
+        proc2.wait()
+        if opt:
+            proc3.wait()
+    logger(message='MoviePy - - File ready: %s.' % filename)
+
+
+def write_gif_with_image_io(clip, filename, fps=None, opt=0, loop=0,
+                            colors=None, verbose=True, logger='bar'):
     """
     Writes the gif with the Python library ImageIO (calls FreeImage).

@@ -90,4 +257,29 @@ def write_gif_with_image_io(clip, filename, fps=None, opt=0, loop=0, colors
     opt

     """
-    pass
+
+    if colors is None:
+        colors = 256
+    logger = proglog.default_bar_logger(logger)
+
+    if not IMAGEIO_FOUND:
+        raise ImportError("Writing a gif with imageio requires ImageIO installed,"
+                         " with e.g. 'pip install imageio'")
+
+    if fps is None:
+        fps = clip.fps
+
+    quantizer = 0 if opt != 0 else 'nq'
+
+    writer = imageio.save(
+        filename,
+        duration=1.0/fps,
+        quantizer=quantizer,
+        palettesize=colors,
+        loop=loop
+        )
+    logger(message='MoviePy - Building file %s with imageio.' % filename)
+
+    for frame in clip.iter_frames(fps=fps, logger=logger, dtype='uint8'):
+
+        writer.append_data(frame)
diff --git a/moviepy/video/io/html_tools.py b/moviepy/video/io/html_tools.py
index d4ecc6b..f24af51 100644
--- a/moviepy/video/io/html_tools.py
+++ b/moviepy/video/io/html_tools.py
@@ -2,34 +2,46 @@
 This module implements ipython_display
 A function to embed images/videos/audio in the IPython Notebook
 """
+
+# Notes:
+# All media are physically embedded in the IPython Notebook
+# (instead of simple links to the original files)
+# That is because most browsers use a cache system and they won't
+# properly refresh the media when the original files are changed.
+
 import os
 from base64 import b64encode
+
 from moviepy.audio.AudioClip import AudioClip
 from moviepy.tools import extensions_dict
+
 from ..VideoClip import ImageClip, VideoClip
 from .ffmpeg_reader import ffmpeg_parse_infos
+
 try:
     from IPython.display import HTML
     ipython_available = True
-
-
     class HTML2(HTML):
-
         def __add__(self, other):
-            return HTML2(self.data + other.data)
+            return HTML2(self.data+other.data)
+
 except ImportError:
     ipython_available = False
-sorry = "Sorry, seems like your browser doesn't support HTML5 audio/video"
-templates = {'audio': 
-    "<audio controls><source %(options)s  src='data:audio/%(ext)s;base64,%(data)s'>"
-     + sorry + '</audio>', 'image':
-    "<img %(options)s src='data:image/%(ext)s;base64,%(data)s'>", 'video': 
-    "<video %(options)ssrc='data:video/%(ext)s;base64,%(data)s' controls>" +
-    sorry + '</video>'}


-def html_embed(clip, filetype=None, maxduration=60, rd_kwargs=None, center=
-    True, **html_kwargs):
+sorry = "Sorry, seems like your browser doesn't support HTML5 audio/video"
+templates = {"audio":("<audio controls>"
+                         "<source %(options)s  src='data:audio/%(ext)s;base64,%(data)s'>"
+                     +sorry+"</audio>"),
+             "image":"<img %(options)s "
+                     "src='data:image/%(ext)s;base64,%(data)s'>",
+             "video":("<video %(options)s"
+                       "src='data:video/%(ext)s;base64,%(data)s' controls>"
+                       +sorry+"</video>")}
+
+
+def html_embed(clip, filetype=None, maxduration=60, rd_kwargs=None,
+               center=True, **html_kwargs):
     """ Returns HTML5 code embedding the clip

     clip
@@ -67,12 +79,85 @@ def html_embed(clip, filetype=None, maxduration=60, rd_kwargs=None, center=
     >>> clip.save_frame("first_frame.jpeg")
     >>> mpy.ipython_display("first_frame.jpeg")

-    """
-    pass
+    """  
+    
+    if rd_kwargs is None:
+        rd_kwargs = {}
+
+    if "Clip" in str(clip.__class__):
+        TEMP_PREFIX = "__temp__"
+        if isinstance(clip,ImageClip):
+            filename = TEMP_PREFIX+".png"
+            kwargs = {'filename':filename, 'withmask':True}
+            kwargs.update(rd_kwargs)
+            clip.save_frame(**kwargs)
+        elif isinstance(clip,VideoClip):
+            filename = TEMP_PREFIX+".mp4"
+            kwargs = {'filename':filename, 'verbose':False, 'preset':'ultrafast'}
+            kwargs.update(rd_kwargs)
+            clip.write_videofile(**kwargs)
+        elif isinstance(clip,AudioClip):
+            filename = TEMP_PREFIX+".mp3"
+            kwargs = {'filename': filename, 'verbose':False}
+            kwargs.update(rd_kwargs)
+            clip.write_audiofile(**kwargs)
+        else:
+          raise ValueError("Unknown class for the clip. Cannot embed and preview.")
+
+        return html_embed(filename, maxduration=maxduration, rd_kwargs=rd_kwargs,
+                           center=center, **html_kwargs)
+    
+    filename = clip
+    options = " ".join(["%s='%s'"%(str(k), str(v)) for k,v in html_kwargs.items()])
+    name, ext = os.path.splitext(filename)
+    ext = ext[1:]
+
+    if filetype is None:
+        ext = filename.split('.')[-1].lower()
+        if ext == "gif":
+            filetype = 'image'
+        elif ext in extensions_dict:
+            filetype = extensions_dict[ext]['type']
+        else:
+            raise ValueError("No file type is known for the provided file. Please provide "
+                             "argument `filetype` (one of 'image', 'video', 'sound') to the "
+                             "ipython display function.")
+    
+    
+    if filetype== 'video':
+        # The next lines set the HTML5-cvompatible extension and check that the
+        # extension is HTML5-valid
+        exts_htmltype = {'mp4': 'mp4', 'webm':'webm', 'ogv':'ogg'}
+        allowed_exts = " ".join(exts_htmltype.keys()) 
+        try:
+            ext = exts_htmltype[ext]
+        except:
+            raise ValueError("This video extension cannot be displayed in the "
+                   "IPython Notebook. Allowed extensions: "+allowed_exts)
+    
+    if filetype in ['audio', 'video']:
+
+        duration = ffmpeg_parse_infos(filename)['duration']
+        if duration > maxduration:
+            raise ValueError("The duration of video %s (%.1f) exceeds the 'maxduration' "%(filename, duration)+
+                             "attribute. You can increase 'maxduration', by passing 'maxduration' parameter"
+                             "to ipython_display function."
+                             "But note that embedding large videos may take all the memory away !")
+            
+    with open(filename, "rb") as f:
+        data= b64encode(f.read()).decode("utf-8")
+
+    template = templates[filetype]
+
+    result = template%{'data':data, 'options':options, 'ext':ext}
+    if center:
+        result = r"<div align=middle>%s</div>"%result
+
+    return result


 def ipython_display(clip, filetype=None, maxduration=60, t=None, fps=None,
-    rd_kwargs=None, center=True, **html_kwargs):
+                    rd_kwargs=None, center=True, **html_kwargs):
     """
     clip
       Either the name of a file, or a clip to preview. The clip will
@@ -119,4 +204,18 @@ def ipython_display(clip, filetype=None, maxduration=60, t=None, fps=None,
     >>> clip.save_frame("first_frame.jpeg")
     >>> mpy.ipython_display("first_frame.jpeg")
     """
-    pass
+        
+    if not ipython_available:
+        raise ImportError("Only works inside an IPython Notebook")
+
+    if rd_kwargs is None:
+        rd_kwargs = {}
+        
+    if fps is not None:
+        rd_kwargs['fps'] = fps
+
+    if t is not None:
+        clip = clip.to_ImageClip(t)
+
+    return HTML2(html_embed(clip, filetype=filetype, maxduration=maxduration,
+                center=center, rd_kwargs=rd_kwargs, **html_kwargs))
diff --git a/moviepy/video/io/preview.py b/moviepy/video/io/preview.py
index 0cb06a5..fbab22d 100644
--- a/moviepy/video/io/preview.py
+++ b/moviepy/video/io/preview.py
@@ -1,16 +1,23 @@
 import threading
 import time
+
 import numpy as np
+
 import pygame as pg
 from moviepy.decorators import convert_masks_to_RGB, requires_duration
 from moviepy.tools import cvsecs
+
 pg.init()
 pg.display.set_caption('MoviePy')


 def imdisplay(imarray, screen=None):
     """Splashes the given image array on the given pygame screen """
-    pass
+    a = pg.surfarray.make_surface(imarray.swapaxes(0, 1))
+    if screen is None:
+        screen = pg.display.set_mode(imarray.shape[:2][::-1])
+    screen.blit(a, (0, 0))
+    pg.display.flip()


 @convert_masks_to_RGB
@@ -29,13 +36,37 @@ def show(clip, t=0, with_mask=True, interactive=False):
       without the mask.

     """
-    pass
+
+    if isinstance(t, tuple):
+        t = cvsecs(*t)
+
+    if with_mask and (clip.mask is not None):
+        import moviepy.video.compositing.CompositeVideoClip as cvc
+        clip = cvc.CompositeVideoClip([clip.set_position((0, 0))])
+    img = clip.get_frame(t)
+    imdisplay(img)
+
+    if interactive:
+        result = []
+        while True:
+            for event in pg.event.get():
+                if event.type == pg.KEYDOWN:
+                    if event.key == pg.K_ESCAPE:
+                        print("Keyboard interrupt")
+                        return result
+                elif event.type == pg.MOUSEBUTTONDOWN:
+                    x, y = pg.mouse.get_pos()
+                    rgb = img[y, x]
+                    result.append({'position': (x, y), 'color': rgb})
+                    print("position, color : ", "%s, %s" %
+                          (str((x, y)), str(rgb)))
+            time.sleep(.03)


 @requires_duration
 @convert_masks_to_RGB
-def preview(clip, fps=15, audio=True, audio_fps=22050, audio_buffersize=
-    3000, audio_nbytes=2, fullscreen=False):
+def preview(clip, fps=15, audio=True, audio_fps=22050, audio_buffersize=3000,
+            audio_nbytes=2, fullscreen=False):
     """ 
     Displays the clip in a window, at the given frames per second
     (of movie) rate. It will avoid that the clip be played faster
@@ -60,4 +91,61 @@ def preview(clip, fps=15, audio=True, audio_fps=22050, audio_buffersize=
       ``True`` if you want the preview to be displayed fullscreen.

     """
-    pass
+    if fullscreen:
+        flags = pg.FULLSCREEN
+    else:
+        flags = 0
+    
+    # compute and splash the first image
+    screen = pg.display.set_mode(clip.size, flags)
+    
+    audio = audio and (clip.audio is not None)
+    
+    if audio:
+        # the sound will be played in parrallel. We are not
+        # parralellizing it on different CPUs because it seems that
+        # pygame and openCV already use several cpus it seems.
+        
+        # two synchro-flags to tell whether audio and video are ready
+        videoFlag = threading.Event()
+        audioFlag = threading.Event()
+        # launch the thread
+        audiothread = threading.Thread(target=clip.audio.preview,
+                                       args=(audio_fps,
+                                             audio_buffersize,
+                                             audio_nbytes,
+                                             audioFlag, videoFlag))
+        audiothread.start()
+    
+    img = clip.get_frame(0)
+    imdisplay(img, screen)
+    if audio:  # synchronize with audio
+        videoFlag.set()  # say to the audio: video is ready
+        audioFlag.wait()  # wait for the audio to be ready
+    
+    result = []
+    
+    t0 = time.time()
+    for t in np.arange(1.0 / fps, clip.duration-.001, 1.0 / fps):
+        
+        img = clip.get_frame(t)
+        
+        for event in pg.event.get():
+            if event.type == pg.QUIT or \
+                    (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
+                if audio:
+                    videoFlag.clear()
+                print("Interrupt")
+                return result
+                    
+            elif event.type == pg.MOUSEBUTTONDOWN:
+                x, y = pg.mouse.get_pos()
+                rgb = img[y, x]
+                result.append({'time': t, 'position': (x, y),
+                               'color': rgb})
+                print("time, position, color : ", "%.03f, %s, %s" %
+                      (t, str((x, y)), str(rgb)))
+                    
+        t1 = time.time()
+        time.sleep(max(0, t - (t1-t0)))
+        imdisplay(img, screen)
diff --git a/moviepy/video/io/sliders.py b/moviepy/video/io/sliders.py
index ac234a1..f94eae0 100644
--- a/moviepy/video/io/sliders.py
+++ b/moviepy/video/io/sliders.py
@@ -2,7 +2,7 @@ import matplotlib.pyplot as plt
 from matplotlib.widgets import Button, Slider


-def sliders(f, sliders_properties, wait_for_validation=False):
+def sliders(f, sliders_properties, wait_for_validation = False):
     """ A light GUI to manually explore and tune the outputs of 
         a function.
         slider_properties is a list of dicts (arguments for Slider )
@@ -15,4 +15,60 @@ def sliders(f, sliders_properties, wait_for_validation=False):
                   { 'label' :  'depth',  'valmin': 1 , 'valmax': 5 } ]
         inputExplorer(volume,intervals)
     """
-    pass
+        
+    nVars = len(sliders_properties)
+    slider_width = 1.0/nVars
+    
+    # CREATE THE CANVAS
+    
+    figure,ax = plt.subplots(1)
+    figure.canvas.set_window_title( "Inputs for '%s'"%(f.func_name) )
+    
+    # choose an appropriate height
+    
+    width,height = figure.get_size_inches()
+    height = min(0.5*nVars,8)
+    figure.set_size_inches(width,height,forward = True)
+    
+    
+    # hide the axis
+    ax.set_frame_on(False)
+    ax.get_xaxis().set_visible(False)
+    ax.get_yaxis().set_visible(False)
+    
+
+    # CREATE THE SLIDERS
+    
+    sliders = []
+    
+    for i, properties in enumerate(sliders_properties):    
+        ax = plt.axes([0.1 , 0.95-0.9*(i+1)*slider_width,
+                       0.8 , 0.8* slider_width])
+        if not isinstance(properties,dict):
+            properties =dict(zip(['label','valmin', 'valmax', 'valinit'],
+                             properties))
+        sliders.append( Slider(ax=ax, **properties) )
+    
+    
+    # CREATE THE CALLBACK FUNCTIONS
+    
+    def on_changed(event) :     
+        res = f(*(s.val for s in sliders))
+        if res is not None:
+            print( res )
+    
+    def on_key_press(event):
+        if event.key is 'enter':
+            on_changed(event)   
+    
+    figure.canvas.mpl_connect('key_press_event', on_key_press)
+    
+    # AUTOMATIC UPDATE ?
+    
+    if not wait_for_validation:
+        for s in sliders :
+            s.on_changed(on_changed)
+    
+    
+    # DISPLAY THE SLIDERS
+    plt.show()
diff --git a/moviepy/video/tools/credits.py b/moviepy/video/tools/credits.py
index ee5dfb2..a5adfbb 100644
--- a/moviepy/video/tools/credits.py
+++ b/moviepy/video/tools/credits.py
@@ -3,13 +3,14 @@ This module contains different functions to make end and opening
 credits, even though it is difficult to fill everyone needs in this
 matter.
 """
+
 from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
 from moviepy.video.fx.resize import resize
 from moviepy.video.VideoClip import ImageClip, TextClip


-def credits1(creditfile, width, stretch=30, color='white', stroke_color=
-    'black', stroke_width=2, font='Impact-Normal', fontsize=60, gap=0):
+def credits1(creditfile, width, stretch=30, color='white', stroke_color='black',
+             stroke_width=2, font='Impact-Normal', fontsize=60, gap=0):
     """

     Parameters
@@ -70,4 +71,48 @@ def credits1(creditfile, width, stretch=30, color='white', stroke_color=
                 Music Supervisor    JEAN DIDIER

     """
-    pass
+
+    # PARSE THE TXT FILE
+    texts = []
+    oneline = True
+    
+    with open(creditfile) as f:
+        for l in f:
+            if l.startswith(('\n', '#')):
+                # exclude blank lines or comments
+                continue
+            elif l.startswith('.blank'):
+                # ..blank n  
+                for i in range(int(l.split(' ')[1])):
+                    texts.append(['\n', '\n'])
+            elif l.startswith('..'):
+                texts.append([l[2:], ''])
+                oneline = True
+            elif oneline:
+                texts.append(['', l])
+                oneline = False
+            else:
+                texts.append(['\n', l])
+       
+    left, right = ("".join(l) for l in zip(*texts))
+    
+    # MAKE TWO COLUMNS FOR THE CREDITS    
+    left, right = [TextClip(txt, color=color, stroke_color=stroke_color,
+                            stroke_width=stroke_width, font=font,
+                            fontsize=fontsize, align=al)
+                   for txt, al in [(left, 'East'), (right, 'West')]]
+
+    cc = CompositeVideoClip([left, right.set_position((left.w + gap, 0))],
+                            size=(left.w + right.w + gap, right.h),
+                            bg_color=None)
+    
+    # SCALE TO THE REQUIRED SIZE
+    
+    scaled = resize(cc, width=width)
+    
+    # TRANSFORM THE WHOLE CREDIT CLIP INTO AN ImageCLip
+    
+    imclip = ImageClip(scaled.get_frame(0))
+    amask = ImageClip(scaled.mask.get_frame(0), ismask=True)
+    
+    return imclip.set_mask(amask)
diff --git a/moviepy/video/tools/cuts.py b/moviepy/video/tools/cuts.py
index 9587e45..de75f78 100644
--- a/moviepy/video/tools/cuts.py
+++ b/moviepy/video/tools/cuts.py
@@ -1,14 +1,22 @@
 """ This module contains everything that can help automatize
 the cuts in MoviePy """
+
 from collections import defaultdict
+
 import numpy as np
+
 from moviepy.decorators import use_clip_fps_by_default


 @use_clip_fps_by_default
-def find_video_period(clip, fps=None, tmin=0.3):
+def find_video_period(clip,fps=None,tmin=.3):
     """ Finds the period of a video based on frames correlation """
-    pass
+    
+    frame = lambda t: clip.get_frame(t).flatten()
+    tt = np.arange(tmin, clip.duration, 1.0/ fps)[1:]
+    ref = frame(0)
+    corrs = [ np.corrcoef(ref, frame(t))[0,1] for t in tt]
+    return tt[np.argmax(corrs)]


 class FramesMatch:
@@ -36,15 +44,16 @@ class FramesMatch:
         self.t2 = t2
         self.d_min = d_min
         self.d_max = d_max
-        self.time_span = t2 - t1
+        self.time_span = t2-t1

     def __str__(self):
-        return '(%.04f, %.04f, %.04f, %.04f)' % (self.t1, self.t2, self.
-            d_min, self.d_max)
+
+        return '(%.04f, %.04f, %.04f, %.04f)'%(
+                self.t1, self.t2, self.d_min, self.d_max)

     def __repr__(self):
-        return '(%.04f, %.04f, %.04f, %.04f)' % (self.t1, self.t2, self.
-            d_min, self.d_max)
+        return '(%.04f, %.04f, %.04f, %.04f)'%(
+                self.t1, self.t2, self.d_min, self.d_max)

     def __iter__(self):
         return iter((self.t1, self.t2, self.d_min, self.d_max))
@@ -53,8 +62,14 @@ class FramesMatch:
 class FramesMatches(list):

     def __init__(self, lst):
+
         list.__init__(self, sorted(lst, key=lambda e: e.d_max))

+    def best(self, n=1, percent=None):
+        if percent is not None:
+            n = len(self)*percent/100
+        return self[0] if n==1 else FramesMatches(self[:n])
+    
     def filter(self, cond):
         """
         Returns a FramesMatches object obtained by filtering out the FramesMatch
@@ -66,15 +81,23 @@ class FramesMatches(list):
         >>> # Only keep the matches corresponding to (> 1 second) sequences.
         >>> new_matches = matches.filter( lambda match: match.time_span > 1)
         """
-        pass
+        return FramesMatches(filter(cond, self))
+
+    def save(self, filename):
+        np.savetxt(filename, np.array([np.array(list(e)) for e in self]),
+                   fmt='%.03f', delimiter='\t')

     @staticmethod
     def load(filename):
         """ Loads a FramesMatches object from a file.
         >>> matching_frames = FramesMatches.load("somefile")
         """
-        pass
+        arr = np.loadtxt(filename)
+        mfs = [FramesMatch(*e) for e in arr]
+        return FramesMatches(mfs)

+        
+    
     @staticmethod
     def from_clip(clip, dist_thr, max_d, fps=None):
         """ Finds all the frames tht look alike in a clip, for instance to make a
@@ -113,11 +136,71 @@ class FramesMatches(list):
         fps
           Frames per second (default will be clip.fps)

-        """
-        pass
+        """ 
+        
+        N_pixels = clip.w * clip.h * 3
+        dot_product = lambda F1, F2: (F1*F2).sum()/N_pixels
+        F = {} # will store the frames and their mutual distances
+        
+        def distance(t1, t2):
+            uv = dot_product(F[t1]['frame'], F[t2]['frame'])
+            u, v = F[t1]['|F|sq'], F[t2]['|F|sq']
+            return np.sqrt(u+v - 2*uv)
+        
+        matching_frames = [] # the final result.
+        
+        for (t,frame) in clip.iter_frames(with_times=True, logger='bar'):
+            
+            flat_frame = 1.0*frame.flatten()
+            F_norm_sq = dot_product(flat_frame, flat_frame)
+            F_norm = np.sqrt(F_norm_sq)
+            
+            for t2 in list(F.keys()):
+                # forget old frames, add 't' to the others frames
+                # check for early rejections based on differing norms
+                if (t-t2) > max_d:
+                    F.pop(t2)
+                else:
+                    F[t2][t] = {'min':abs(F[t2]['|F|'] - F_norm),
+                                'max':F[t2]['|F|'] + F_norm}
+                    F[t2][t]['rejected']= (F[t2][t]['min'] > dist_thr)
+            
+            t_F = sorted(F.keys())
+            
+            F[t] = {'frame': flat_frame, '|F|sq': F_norm_sq, '|F|': F_norm}
+                    
+            for i,t2 in enumerate(t_F):
+                # Compare F(t) to all the previous frames
+                
+                if F[t2][t]['rejected']:
+                    continue
+                
+                dist = distance(t, t2)
+                F[t2][t]['min'] = F[t2][t]['max'] = dist
+                F[t2][t]['rejected']  = (dist >= dist_thr)
+                
+                for t3 in t_F[i+1:]:
+                    # For all the next times t3, use d(F(t), F(t2)) to
+                    # update the bounds on d(F(t), F(t3)). See if you can
+                    # conclude on wether F(t) and F(t3) match.
+                    t3t, t2t3 = F[t3][t], F[t2][t3]
+                    t3t['max'] = min(t3t['max'], dist+ t2t3['max'])
+                    t3t['min'] = max(t3t['min'], dist - t2t3['max'],
+                                     t2t3['min'] - dist)
+                                          
+                    if t3t['min'] > dist_thr:
+                        t3t['rejected'] = True
+        
+            # Store all the good matches (t2,t)
+            matching_frames += [(t1, t, F[t1][t]['min'], F[t1][t]['max']) for t1 in F
+                                if (t1!=t) and not F[t1][t]['rejected']]
+                       
+        return FramesMatches([FramesMatch(*e) for e in matching_frames])
+
+

     def select_scenes(self, match_thr, min_time_span, nomatch_thr=None,
-        time_distance=0):
+                      time_distance=0):
         """

         match_thr
@@ -131,12 +214,62 @@ class FramesMatches(list):
           If None, then it is chosen equal to match_thr

         """
-        pass
+
+        if nomatch_thr is None:
+            nomatch_thr = match_thr
+
+        dict_starts = defaultdict(lambda : [])
+        for (start, end, d_min, d_max) in self:
+            dict_starts[start].append([end, d_min, d_max])
+
+        starts_ends = sorted(dict_starts.items(), key = lambda k: k[0])
+        
+        result = []
+        min_start= 0
+        for start, ends_distances in starts_ends:
+
+            if start < min_start:
+                continue
+
+            ends = [end for (end, d_min, d_max) in ends_distances]
+            great_matches = [(end,d_min, d_max)
+                             for (end,d_min, d_max) in ends_distances
+                             if d_max<match_thr]
+            
+            great_long_matches = [(end,d_min, d_max)
+                                  for (end,d_min, d_max) in great_matches
+                                  if (end-start)>min_time_span]
+            
+            
+            if not great_long_matches:
+                continue # No GIF can be made starting at this time
+            
+            poor_matches = {end for (end,d_min, d_max) in ends_distances if d_min > nomatch_thr}
+            short_matches = {end for end in ends if (end-start) <= 0.6}
+            
+            if not poor_matches.intersection(short_matches):
+                continue
+    
+            end = max(end for (end, d_min, d_max) in great_long_matches)
+            end, d_min, d_max = next(e for e in great_long_matches if e[0]==end)
+            
+            result.append(FramesMatch(start, end, d_min, d_max))
+            min_start = start + time_distance
+
+        return FramesMatches(result)
+
+
+    def write_gifs(self, clip, gif_dir):
+        for (start, end, _, _) in self: 
+            name = "%s/%08d_%08d.gif" % (gif_dir, 100*start, 100*end)
+            clip.subclip(start, end).write_gif(name, verbose=False)
+
+


 @use_clip_fps_by_default
-def detect_scenes(clip=None, luminosities=None, thr=10, logger='bar', fps=None
-    ):
+def detect_scenes(clip=None, luminosities=None, thr=10,
+                  logger='bar', fps=None):
     """ Detects scenes of a clip based on luminosity changes.

     Note that for large clip this may take some time
@@ -176,5 +309,20 @@ def detect_scenes(clip=None, luminosities=None, thr=10, logger='bar', fps=None
       fps attribute.


-    """
-    pass
+    """       
+    if luminosities is None:
+        luminosities = [f.sum() for f in clip.iter_frames(
+                             fps=fps, dtype='uint32', logger=logger)]
+    
+    luminosities = np.array(luminosities, dtype=float)
+    if clip is not None:
+        end = clip.duration
+    else:
+        end = len(luminosities)*(1.0/fps) 
+    lum_diffs = abs(np.diff(luminosities))
+    avg = lum_diffs.mean()
+    luminosity_jumps = 1+np.array(np.nonzero(lum_diffs> thr*avg))[0]
+    tt = [0]+list((1.0/fps) *luminosity_jumps) + [end]
+    #print tt
+    cuts = [(t1,t2) for t1,t2 in zip(tt,tt[1:])]
+    return cuts, luminosities
diff --git a/moviepy/video/tools/drawing.py b/moviepy/video/tools/drawing.py
index 262e880..fdc3705 100644
--- a/moviepy/video/tools/drawing.py
+++ b/moviepy/video/tools/drawing.py
@@ -2,6 +2,7 @@
 This module deals with making images (np arrays). It provides drawing
 methods that are difficult to do with the existing Python libraries.
 """
+
 import numpy as np


@@ -12,11 +13,45 @@ def blit(im1, im2, pos=None, mask=None, ismask=False):
     ``mask`` if provided. If ``im1`` and ``im2`` are mask pictures
     (2D float arrays) then ``ismask`` must be ``True``.
     """
-    pass
+    if pos is None:
+        pos = [0, 0]
+
+    # xp1,yp1,xp2,yp2 = blit area on im2
+    # x1,y1,x2,y2 = area of im1 to blit on im2
+    xp, yp = pos
+    x1 = max(0, -xp)
+    y1 = max(0, -yp)
+    h1, w1 = im1.shape[:2]
+    h2, w2 = im2.shape[:2]
+    xp2 = min(w2, xp + w1)
+    yp2 = min(h2, yp + h1)
+    x2 = min(w1, w2 - xp)
+    y2 = min(h1, h2 - yp)
+    xp1 = max(0, xp)
+    yp1 = max(0, yp)
+
+    if (xp1 >= xp2) or (yp1 >= yp2):
+        return im2
+
+    blitted = im1[y1:y2, x1:x2]
+
+    new_im2 = +im2
+
+    if mask is None:
+        new_im2[yp1:yp2, xp1:xp2] = blitted
+    else:
+        mask = mask[y1:y2, x1:x2]
+        if len(im1.shape) == 3:
+            mask = np.dstack(3 * [mask])
+        blit_region = new_im2[yp1:yp2, xp1:xp2]
+        new_im2[yp1:yp2, xp1:xp2] = (1.0 * mask * blitted + (1.0 - mask) * blit_region)
+    
+    return new_im2.astype('uint8') if (not ismask) else new_im2


-def color_gradient(size, p1, p2=None, vector=None, r=None, col1=0, col2=1.0,
-    shape='linear', offset=0):
+
+def color_gradient(size,p1,p2=None,vector=None, r=None, col1=0,col2=1.0,
+                   shape='linear', offset = 0):
     """Draw a linear, bilinear, or radial gradient.

     The result is a picture of size ``size``, whose color varies
@@ -79,11 +114,70 @@ def color_gradient(size, p1, p2=None, vector=None, r=None, col1=0, col2=1.0,
     >>> grad = color_gradient(blabla).astype('uint8')

     """
-    pass
+    
+    # np-arrayize and change x,y coordinates to y,x
+    w,h = size
+    
+    col1 = np.array(col1).astype(float) 
+    col2 = np.array(col2).astype(float) 
+    
+    if shape == 'bilinear':
+        if vector is None:
+            vector = np.array(p2) - np.array(p1)
+        
+        m1, m2 = [ color_gradient(size, p1, vector=v, col1 = 1.0, col2 = 0,
+                                 shape = 'linear', offset= offset)
+                            for v in [vector,-vector]]
+                            
+        arr = np.maximum(m1, m2)
+        if col1.size > 1:
+            arr = np.dstack(3*[arr])
+        return arr*col1 + (1-arr)*col2
+        
+    
+    p1 = np.array(p1[::-1]).astype(float)
+    
+    if vector is None and p2:
+        p2 = np.array(p2[::-1])
+        vector = p2-p1
+    else:
+        vector = np.array(vector[::-1])
+        p2 = p1 + vector
+    
+    if vector:    
+        norm = np.linalg.norm(vector)
+    
+    M = np.dstack(np.meshgrid(range(w),range(h))[::-1]).astype(float)
+    
+    if shape == 'linear':
+        
+        n_vec = vector/norm**2 # norm 1/norm(vector)
+        
+        p1 = p1 + offset*vector
+        arr = (M- p1).dot(n_vec)/(1-offset)
+        arr = np.minimum(1,np.maximum(0,arr))
+        if col1.size > 1:
+            arr = np.dstack(3*[arr])
+        return arr*col1 + (1-arr)*col2
+    
+    elif shape == 'radial':
+        if r is None:
+           r = norm

+        if r == 0:
+            arr = np.ones((h,w))
+        else:
+            arr = (np.sqrt(((M - p1) ** 2).sum(axis=2))) - offset * r
+            arr = arr / ((1-offset)*r)
+            arr = np.minimum(1.0, np.maximum(0, arr))
+                
+        if col1.size > 1:
+            arr = np.dstack(3*[arr])
+        return (1-arr)*col1 + arr*col2
+                

-def color_split(size, x=None, y=None, p1=None, p2=None, vector=None, col1=0,
-    col2=1.0, grad_width=0):
+def color_split(size,x=None,y=None,p1=None,p2=None,vector=None,
+                             col1=0,col2=1.0, grad_width=0):
     """Make an image splitted in 2 colored regions.

     Returns an array of size ``size`` divided in two regions called 1 and
@@ -129,9 +223,39 @@ def color_split(size, x=None, y=None, p1=None, p2=None, vector=None, col1=0,
     >>> color_split(size, p1=[20,50], p2=[25,70] col1=0, col2=1)

     """
-    pass
-
+    
+    if grad_width or ( (x is None) and (y is None)):
+        if p2 is not None:
+            vector = (np.array(p2) - np.array(p1))
+        elif x is not None:
+            vector = np.array([0,-1.0])
+            p1 = np.array([x, 0])
+        elif y is not None:
+            vector = np.array([1.0, 0.0])
+            p1 = np.array([0,y])

+        x,y = vector
+        vector = np.array([y,-x]).astype('float')
+        norm = np.linalg.norm(vector)
+        vector = max(0.1, grad_width) * vector / norm
+        return color_gradient(size,p1,vector=vector,
+                              col1 = col1, col2 = col2, shape='linear')
+    else:
+        w, h = size
+        shape = (h, w) if np.isscalar(col1) else (h, w, len(col1))
+        arr = np.zeros(shape)
+        if x:
+            arr[:,:x] = col1
+            arr[:,x:] = col2
+        elif y:
+            arr[:y] = col1
+            arr[y:] = col2
+        return arr
+     
+    # if we are here, it means we didn't exit with a proper 'return'
+    print( "Arguments in color_split not understood !" )
+    raise
+        
 def circle(screensize, center, radius, col1=1.0, col2=0, blur=1):
     """ Draw an image with a circle.

@@ -140,4 +264,6 @@ def circle(screensize, center, radius, col1=1.0, col2=0, blur=1):
     with a radius ``radius`` but slightly blurred on the border by ``blur``
     pixels
     """
-    pass
+    offset = 1.0*(radius-blur)/radius if radius else 0              
+    return color_gradient(screensize,p1=center,r=radius, col1=col1,
+                          col2=col2, shape='radial', offset=offset)
diff --git a/moviepy/video/tools/interpolators.py b/moviepy/video/tools/interpolators.py
index e2d435d..9d1f495 100644
--- a/moviepy/video/tools/interpolators.py
+++ b/moviepy/video/tools/interpolators.py
@@ -2,17 +2,20 @@
 Classes for easy interpolation of trajectories and Curves.
 Requires Scipy installed.
 """
+
 import numpy as np


 class Interpolator:
     """ Poorman's linear interpolator, doesn't require Scipy. """
+    
+    def __init__(self, tt=None, ss=None, ttss = None, left=None, right=None):

-    def __init__(self, tt=None, ss=None, ttss=None, left=None, right=None):
         if ttss is not None:
             tt, ss = zip(*ttss)
-        self.tt = 1.0 * np.array(tt)
-        self.ss = 1.0 * np.array(ss)
+        
+        self.tt = 1.0*np.array(tt)
+        self.ss = 1.0*np.array(ss)
         self.left = left
         self.right = right
         self.tmin, self.tmax = min(tt), max(tt)
@@ -20,14 +23,51 @@ class Interpolator:
     def __call__(self, t):
         return np.interp(t, self.tt, self.ss, self.left, self.right)

-
 class Trajectory:

     def __init__(self, tt, xx, yy):
-        self.tt = 1.0 * np.array(tt)
+
+        self.tt = 1.0*np.array(tt)
         self.xx = np.array(xx)
         self.yy = np.array(yy)
         self.update_interpolators()

     def __call__(self, t):
         return np.array([self.xi(t), self.yi(t)])
+
+    def addx(self, x):
+        return Trajectory(self.tt, self.xx+x, self.yy)
+
+    def addy(self, y):
+        return Trajectory(self.tt, self.xx, self.yy+y)
+
+    def update_interpolators(self):
+        self.xi =  Interpolator(self.tt, self.xx)
+        self.yi =  Interpolator(self.tt, self.yy)
+    
+    def txy(self, tms=False):
+        return zip((1000 if tms else 1)*self.tt, self.xx, self.yy)
+
+    def to_file(self, filename):
+        np.savetxt(filename, np.array(self.txy(tms=True)),
+                   fmt="%d", delimiter='\t')
+
+    @staticmethod
+    def from_file(filename):
+        arr = np.loadtxt(filename, delimiter='\t')
+        tt, xx, yy = arr.T
+        return Trajectory(1.0*tt/1000, xx, yy)
+
+    @staticmethod
+    def save_list(trajs, filename):
+        N = len(trajs)
+        arr = np.hstack([np.array(list(t.txy(tms=True))) for t in trajs])
+        np.savetxt( filename, arr, fmt="%d", delimiter='\t',
+                    header = "\t".join(N*['t(ms)', 'x', 'y']))
+    
+    @staticmethod
+    def load_list(filename):
+        arr = np.loadtxt(filename, delimiter='\t').T
+        Nlines = arr.shape[0]
+        return [Trajectory(tt=1.0*a[0]/1000, xx=a[1], yy=a[2])
+                for a in np.split(arr, Nlines/3)]
diff --git a/moviepy/video/tools/segmenting.py b/moviepy/video/tools/segmenting.py
index 39b1c9e..1e6c06b 100644
--- a/moviepy/video/tools/segmenting.py
+++ b/moviepy/video/tools/segmenting.py
@@ -1,9 +1,10 @@
 import numpy as np
+
 import scipy.ndimage as ndi
 from moviepy.video.VideoClip import ImageClip


-def findObjects(clip, rem_thr=500, preview=False):
+def findObjects(clip,rem_thr=500, preview=False):
     """ 
     Returns a list of ImageClips representing each a separate object on
     the screen.
@@ -12,4 +13,47 @@ def findObjects(clip, rem_thr=500, preview=False):
          considered false positives and will be removed

     """
-    pass
+    
+    image = clip.get_frame(0)
+    if not clip.mask:
+        clip = clip.add_mask()
+        
+    mask = clip.mask.get_frame(0)
+    labelled, num_features = ndi.measurements.label(image[:,:,0])
+    
+    #find the objects
+    slices = []
+    for e in ndi.find_objects(labelled):
+        if mask[e[0],e[1]].mean() <= 0.2:
+            # remove letter holes (in o,e,a, etc.)
+            continue
+        if image[e[0],e[1]].size <= rem_thr:
+            # remove very small slices
+            continue
+        slices.append(e)
+    islices = sorted(enumerate(slices), key = lambda s : s[1][1].start)
+    
+    letters = []
+    for i,(ind,(sy,sx)) in enumerate(islices):
+        """ crop each letter separately """
+        sy = slice(sy.start-1,sy.stop+1)
+        sx = slice(sx.start-1,sx.stop+1)
+        letter = image[sy,sx]
+        labletter = labelled[sy,sx]
+        maskletter = (labletter==(ind+1))*mask[sy,sx]
+        letter = ImageClip(image[sy,sx])
+        letter.mask = ImageClip( maskletter,ismask=True)
+        letter.screenpos = np.array((sx.start,sy.start))
+        letters.append(letter)
+    
+    if preview:
+        import matplotlib.pyplot as plt
+        print( "found %d objects"%(num_features) )
+        fig,ax = plt.subplots(2)
+        ax[0].axis('off')
+        ax[0].imshow(labelled)
+        ax[1].imshow([range(num_features)],interpolation='nearest')
+        ax[1].set_yticks([])
+        plt.show()
+    
+    return letters
diff --git a/moviepy/video/tools/subtitles.py b/moviepy/video/tools/subtitles.py
index b191e4d..34a8615 100644
--- a/moviepy/video/tools/subtitles.py
+++ b/moviepy/video/tools/subtitles.py
@@ -1,6 +1,9 @@
 """ Experimental module for subtitles support. """
+
 import re
+
 import numpy as np
+
 from moviepy.tools import cvsecs
 from moviepy.video.VideoClip import TextClip, VideoClip

@@ -32,70 +35,108 @@ class SubtitlesClip(VideoClip):
     """

     def __init__(self, subtitles, make_textclip=None):
+        
         VideoClip.__init__(self, has_constant_size=False)
+
         if isinstance(subtitles, str):
             subtitles = file_to_subtitles(subtitles)
+
+        #subtitles = [(map(cvsecs, tt),txt) for tt, txt in subtitles]
         self.subtitles = subtitles
         self.textclips = dict()
+
         if make_textclip is None:
             make_textclip = lambda txt: TextClip(txt, font='Georgia-Bold',
-                fontsize=24, color='white', stroke_color='black',
-                stroke_width=0.5)
-        self.make_textclip = make_textclip
-        self.start = 0
-        self.duration = max([tb for (ta, tb), txt in self.subtitles])
-        self.end = self.duration
+                                        fontsize=24, color='white',
+                                        stroke_color='black', stroke_width=0.5)

+        self.make_textclip = make_textclip
+        self.start=0
+        self.duration = max([tb for ((ta,tb), txt) in self.subtitles])
+        self.end=self.duration
+        
         def add_textclip_if_none(t):
             """ Will generate a textclip if it hasn't been generated asked
             to generate it yet. If there is no subtitle to show at t, return
             false. """
-            sub = [((ta, tb), txt) for (ta, tb), txt in self.textclips.keys
-                () if ta <= t < tb]
+            sub =[((ta,tb),txt) for ((ta,tb),txt) in self.textclips.keys()
+                   if (ta<=t<tb)]
             if not sub:
-                sub = [((ta, tb), txt) for (ta, tb), txt in self.subtitles if
-                    ta <= t < tb]
+                sub = [((ta,tb),txt) for ((ta,tb),txt) in self.subtitles if
+                       (ta<=t<tb)]
                 if not sub:
                     return False
             sub = sub[0]
             if sub not in self.textclips.keys():
                 self.textclips[sub] = self.make_textclip(sub[1])
+
             return sub

         def make_frame(t):
             sub = add_textclip_if_none(t)
-            return self.textclips[sub].get_frame(t) if sub else np.array([[
-                [0, 0, 0]]])
+            return (self.textclips[sub].get_frame(t) if sub
+                    else np.array([[[0,0,0]]]))

         def make_mask_frame(t):
             sub = add_textclip_if_none(t)
-            return self.textclips[sub].mask.get_frame(t) if sub else np.array([
-                [0]])
+            return (self.textclips[sub].mask.get_frame(t) if sub
+                    else np.array([[0]]))
+        
         self.make_frame = make_frame
         hasmask = bool(self.make_textclip('T').mask)
-        self.mask = VideoClip(make_mask_frame, ismask=True
-            ) if hasmask else None
+        self.mask = VideoClip(make_mask_frame, ismask=True) if hasmask else None

-    def in_subclip(self, t_start=None, t_end=None):
+    def in_subclip(self, t_start= None, t_end= None):
         """ Returns a sequence of [(t1,t2), txt] covering all the given subclip
         from t_start to t_end. The first and last times will be cropped so as
         to be exactly t_start and t_end if possible. """
-        pass
+
+        def is_in_subclip(t1,t2):
+            try:
+                return (t_start<=t1<t_end) or (t_start< t2 <=t_end)
+            except:
+                return False
+        def try_cropping(t1,t2):
+            try:
+                return (max(t1, t_start), min(t2, t_end))
+            except:
+                return (t1, t2)
+        return [(try_cropping(t1,t2), txt) for ((t1,t2), txt) in self.subtitles
+                                               if is_in_subclip(t1,t2)]
+    
+

     def __iter__(self):
         return iter(self.subtitles)
+    
+

     def __getitem__(self, k):
         return self.subtitles[k]

+    
+
     def __str__(self):

         def to_srt(sub_element):
             (ta, tb), txt = sub_element
             fta = cvsecs(ta)
             ftb = cvsecs(tb)
-            return '%s - %s\n%s' % (fta, ftb, txt)
-        return '\n\n'.join(to_srt(s) for s in self.subtitles)
+            return "%s - %s\n%s"%(fta, ftb, txt)
+        
+        return "\n\n".join(to_srt(s) for s in self.subtitles)
+    
+
+
+    def match_expr(self, expr):
+
+        return SubtitlesClip([e for e in self.subtitles
+                              if re.findall(expr, e[1]) != []])
+    
+
+    def write_srt(self, filename):
+        with open(filename, 'w+') as f:
+            f.write(str(self))


 def file_to_subtitles(filename):
@@ -106,4 +147,17 @@ def file_to_subtitles(filename):

     Only works for '.srt' format for the moment.
     """
-    pass
+    times_texts = []
+    current_times = None
+    current_text = ""
+    with open(filename,'r') as f:
+        for line in f:
+            times = re.findall("([0-9]*:[0-9]*:[0-9]*,[0-9]*)", line)
+            if times:
+                current_times = [cvsecs(t) for t in times]
+            elif line.strip() == '':
+                times_texts.append((current_times, current_text.strip('\n')))
+                current_times, current_text = None, ""
+            elif current_times:
+                current_text += line
+    return times_texts
diff --git a/moviepy/video/tools/tracking.py b/moviepy/video/tools/tracking.py
index f52522c..fd5247f 100644
--- a/moviepy/video/tools/tracking.py
+++ b/moviepy/video/tools/tracking.py
@@ -7,21 +7,31 @@ time t is out of the time bounds of the tracking time interval
 fx and fy return the position of the object at the start or at the end
 of the tracking time interval).
 """
+
 import numpy as np
+
 from moviepy.decorators import convert_to_seconds, use_clip_fps_by_default
+
 from ..io.preview import imdisplay
 from .interpolators import Trajectory
+
 try:
     import cv2
     autotracking_possible = True
 except:
+    # Note: this will be later fixed with scipy/skimage replacements
+    # but for the moment OpenCV is mandatory, so...
     autotracking_possible = False


-@convert_to_seconds(['t1', 't2'])
+# WE START WITH A TOOL FUNCTION
+
+# MANUAL TRACKING
+
+@convert_to_seconds(["t1","t2"])
 @use_clip_fps_by_default
-def manual_tracking(clip, t1=None, t2=None, fps=None, nobjects=1, savefile=None
-    ):
+def manual_tracking(clip, t1=None, t2=None, fps=None, nobjects = 1,
+                    savefile = None):
     """
     Allows manual tracking of an object(s) in the video clip between
     times `t1` and `t2`. This displays the clip frame by frame
@@ -66,17 +76,81 @@ def manual_tracking(clip, t1=None, t2=None, fps=None, nobjects=1, savefile=None
     >>> traj, =  Trajectory.load_list('track.txt')

     """
-    pass
+    
+    import pygame as pg
+
+    screen = pg.display.set_mode(clip.size)
+    step = 1.0 / fps
+    if (t1 is None) and (t2 is None):
+        t1,t2 = 0, clip.duration
+    elif (t2 is None):
+        t2 = t1 + step / 2
+    t = t1
+    txy_list = []
+    
+    def gatherClicks(t):
+        
+        imdisplay(clip.get_frame(t), screen)
+        objects_to_click = nobjects
+        clicks = []
+        while objects_to_click:
+
+            for event in pg.event.get():
+
+                if event.type == pg.KEYDOWN:
+                    if (event.key == pg.K_BACKSLASH):
+                        return "return"
+                    elif (event.key == pg.K_ESCAPE):
+                        raise KeyboardInterrupt()
+                        
+
+                elif event.type == pg.MOUSEBUTTONDOWN:
+                    x, y = pg.mouse.get_pos()
+                    clicks.append((x, y))
+                    objects_to_click -= 1
+                    
+        return clicks
+        
+    while t < t2:
+        
+        clicks  =gatherClicks(t)
+        if clicks == 'return':
+            txy_list.pop()
+            t -= step
+        else:
+            txy_list.append((t,clicks))
+            t += step

+    tt, xylist = zip(*txy_list) 
+    result = []
+    for i in range(nobjects):
+        xys = [e[i] for e in xylist]
+        xx, yy = zip(*xys)
+        result.append(Trajectory(tt, xx, yy))
+    
+    if savefile is not None:
+        Trajectory.save_list(result, savefile)
+    return result
+
+
+# AUTOMATED TRACKING OF A PATTERN

-def findAround(pic, pat, xy=None, r=None):
+def findAround(pic,pat,xy=None,r=None):
     """
     find image pattern ``pat`` in ``pic[x +/- r, y +/- r]``.
     if xy is none, consider the whole picture.
     """
-    pass
-
-
+    
+    if xy and r:
+        h,w = pat.shape[:2]
+        x,y = xy
+        pic = pic[y-r : y+h+r , x-r : x+w+r]
+        
+    matches = cv2.matchTemplate(pat,pic,cv2.TM_CCOEFF_NORMED)
+    yf,xf = np.unravel_index(matches.argmax(),matches.shape)
+    return (x-r+xf,y-r+yf) if (xy and r) else (xf,yf)
+        
+        
 def autoTrack(clip, pattern, tt=None, fps=None, radius=20, xy0=None):
     """
     Tracks a given pattern (small image array) in a video clip.
@@ -90,4 +164,23 @@ def autoTrack(clip, pattern, tt=None, fps=None, radius=20, xy0=None):
     to -1 the pattern will be searched in the whole screen at each frame).
     You can also provide the original position of the pattern with xy0.
     """
-    pass
+
+    if not autotracking_possible:
+        raise IOError("Sorry, autotrack requires OpenCV for the moment. "
+                      "Install OpenCV (aka cv2) to use it.")
+
+
+    if not xy0:
+        xy0 = findAround(clip.get_frame(tt[0]),pattern)
+    
+    if tt is None:
+        tt = np.arange(0, clip.duration, 1.0/fps)
+        
+    xys = [xy0]
+    for t in tt[1:]:
+        xys.append( findAround(clip.get_frame(t),pattern,
+                               xy=xys[-1],r=radius))
+    
+    xx,yy = zip(*xys)
+
+    return Trajectory(tt, xx, yy)