Skip to content

Commit d029beb

Browse files
bugfix: convert non-.wav/.raw files to .wav before passing to pydub (#3832)
pydub will try to use `ffmpeg` for non-`.wav`/`.raw` files, which is no longer a dependency of Manim. To avoid this, we convert non `.wav`/`.raw` files into `.wav` files before passing it to pydub. --------- Co-authored-by: JasonGrace2282 <[email protected]>
1 parent 99ce78f commit d029beb

File tree

3 files changed

+38
-1
lines changed

3 files changed

+38
-1
lines changed

manim/scene/scene_file_writer.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import shutil
99
from pathlib import Path
1010
from queue import Queue
11+
from tempfile import NamedTemporaryFile
1112
from threading import Thread
1213
from typing import TYPE_CHECKING, Any
1314

@@ -330,7 +331,32 @@ def add_sound(
330331
331332
"""
332333
file_path = get_full_sound_file_path(sound_file)
333-
new_segment = AudioSegment.from_file(file_path)
334+
# we assume files with .wav / .raw suffix are actually
335+
# .wav and .raw files, respectively.
336+
if file_path.suffix not in (".wav", ".raw"):
337+
# we need to pass delete=False to work on Windows
338+
# TODO: figure out a way to cache the wav file generated (benchmark needed)
339+
wav_file_path = NamedTemporaryFile(suffix=".wav", delete=False)
340+
with (
341+
av.open(file_path) as input_container,
342+
av.open(wav_file_path, "w", format="wav") as output_container,
343+
):
344+
for audio_stream in input_container.streams.audio:
345+
output_stream = output_container.add_stream("pcm_s16le")
346+
for frame in input_container.decode(audio_stream):
347+
for packet in output_stream.encode(frame):
348+
output_container.mux(packet)
349+
350+
for packet in output_stream.encode():
351+
output_container.mux(packet)
352+
353+
new_segment = AudioSegment.from_file(wav_file_path.name)
354+
logger.info(f"Automatically converted {file_path} to .wav")
355+
wav_file_path.close()
356+
Path(wav_file_path.name).unlink()
357+
else:
358+
new_segment = AudioSegment.from_file(file_path)
359+
334360
if gain:
335361
new_segment = new_segment.apply_gain(gain)
336362
self.add_audio_segment(new_segment, time, **kwargs)

tests/test_scene_rendering/click.mp3

16 KB
Binary file not shown.

tests/test_scene_rendering/test_file_writer.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,17 @@ def test_codecs(tmp_path, format, transparent, codec, pixel_format):
145145
np.testing.assert_allclose(first_frame[-1, -1], target_rgba_center, atol=5)
146146

147147

148+
def test_scene_with_non_raw_or_wav_audio(manim_caplog):
149+
class SceneWithMP3(Scene):
150+
def construct(self):
151+
file_path = Path(__file__).parent / "click.mp3"
152+
self.add_sound(file_path)
153+
self.wait()
154+
155+
SceneWithMP3().render()
156+
assert "click.mp3 to .wav" in manim_caplog.text
157+
158+
148159
@pytest.mark.slow
149160
def test_unicode_partial_movie(tmpdir, simple_scenes_path):
150161
# Characters that failed for a user on Windows

0 commit comments

Comments
 (0)