Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AudioClip I/O #1108

Merged
merged 13 commits into from
May 5, 2020
4 changes: 2 additions & 2 deletions moviepy/audio/AudioClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ def make_frame(t):
is a list of the form sin(t) """

if isinstance(t, np.ndarray):
array_inds = (self.fps*t).astype(int)
in_array = (array_inds > 0) & (array_inds < len(self.array))
array_inds = np.round(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]]
return result
Expand Down
9 changes: 5 additions & 4 deletions moviepy/audio/io/readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ def read_chunk(self,chunksize):
result = (1.0*result / 2**(8*self.nbytes-1)).\
reshape((int(len(result)/self.nchannels),
self.nchannels))
pad = np.zeros((chunksize - len(result), self.nchannels),
dtype=result.dtype)
result = np.concatenate([result, pad])
MaigoAkisame marked this conversation as resolved.
Show resolved Hide resolved
#self.proc.stdout.flush()
self.pos = self.pos+chunksize
return result
Expand Down Expand Up @@ -168,7 +171,7 @@ def get_frame(self, tt):
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)
"with clip duration=%f seconds, "%self.duration)

# The np.round in the next line is super-important.
# Removing it results in artifacts in the noise.
Expand All @@ -187,8 +190,6 @@ def get_frame(self, tt):
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

Expand Down Expand Up @@ -234,7 +235,7 @@ def buffer_around(self,framenumber):
current_f_end <
new_bufferstart + self.buffersize):
# We already have one bit of what must be read
conserved = current_f_end - new_bufferstart + 1
conserved = current_f_end - new_bufferstart
MaigoAkisame marked this conversation as resolved.
Show resolved Hide resolved
chunksize = self.buffersize-conserved
array = self.read_chunk(chunksize)
self.buffer = np.vstack([self.buffer[-conserved:], array])
Expand Down
22 changes: 20 additions & 2 deletions tests/test_AudioClips.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import sys

import pytest
import numpy as np
from numpy import pi, sin

from moviepy.audio.AudioClip import (AudioClip, CompositeAudioClip,
concatenate_audioclips)
from moviepy.audio.AudioClip import (
AudioClip, AudioArrayClip, CompositeAudioClip, concatenate_audioclips
)
from moviepy.audio.io.AudioFileClip import AudioFileClip

from .test_helper import TMP_DIR
Expand All @@ -29,6 +31,22 @@ def test_audioclip():
clip = AudioClip(make_frame, duration=2, fps=22050)
clip.write_audiofile(os.path.join(TMP_DIR, "audioclip.mp3"))

def test_audioclip_io():
# Generate a random audio clip of 4.989 seconds at 44100 Hz,
# and save it to a file.
input_array = np.random.random((220000, 2)) * 1.98 - 0.99
clip = AudioArrayClip(input_array, fps=44100)
clip.write_audiofile(os.path.join(TMP_DIR, "random.wav"))
# Load the clip.
# The loaded clip will be slightly longer because the duration is rounded
# up to 4.99 seconds.
# Verify that the extra frames are all zero, and the remainder is identical
# to the original signal.
clip = AudioFileClip(os.path.join(TMP_DIR, "random.wav"))
output_array = clip.to_soundarray()
assert np.abs(output_array[:len(input_array)] - input_array).max() < 4e-5
assert (output_array[len(input_array):] == 0).all()

def test_audioclip_concat():
make_frame_440 = lambda t: [sin(440 * 2 * pi * t)]
make_frame_880 = lambda t: [sin(880 * 2 * pi * t)]
Expand Down