From 1a3f5ba396cfba21b9edae196d2469462ea26dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian-Robert=20St=C3=B6ter?= Date: Mon, 13 May 2024 09:34:25 +0200 Subject: [PATCH 1/7] add ffprobe check and proper environment variables --- examples/readwrite.py | 98 ++----------------------------------------- stempeg/__init__.py | 2 +- stempeg/cmds.py | 44 ++++++++++++++----- tests/test_read.py | 13 ++++++ 4 files changed, 51 insertions(+), 106 deletions(-) diff --git a/examples/readwrite.py b/examples/readwrite.py index d2f397b..9862f29 100644 --- a/examples/readwrite.py +++ b/examples/readwrite.py @@ -9,33 +9,11 @@ if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument( - 'input', - ) args = parser.parse_args() # load stems - stems, rate = stempeg.read_stems(args.input) - - # load stems, - # resample to 96000 Hz, - # use multiprocessing - stems, rate = stempeg.read_stems( - args.input, - sample_rate=96000, - multiprocess=True - ) + stems, rate = stempeg.read_stems(stempeg.example_stem_path()) - # --> stems now has `shape=(stem x samples x channels)`` - - # save stems from tensor as multi-stream mp4 - stempeg.write_stems( - "test.stem.m4a", - stems, - sample_rate=96000 - ) - - # save stems as dict for convenience stems = { "mix": stems[0], "drums": stems[1], @@ -43,83 +21,13 @@ "other": stems[3], "vocals": stems[4], } - # keys will be automatically used - - # from dict as files - stempeg.write_stems( - "test.stem.m4a", - data=stems, - sample_rate=96000 - ) - # `write_stems` is a preset for the following settings - # here the output signal is resampled to 44100 Hz and AAC codec is used stempeg.write_stems( - "test.stem.m4a", - stems, - sample_rate=96000, - writer=stempeg.StreamsWriter( - codec="aac", - output_sample_rate=44100, - bitrate="256000", - stem_names=['mix', 'drums', 'bass', 'other', 'vocals'] - ) - ) - - # Native Instruments compatible stems - stempeg.write_stems( - "test_traktor.stem.m4a", - stems, - sample_rate=96000, - writer=stempeg.NIStemsWriter( - stems_metadata=[ - {"color": "#009E73", "name": "Drums"}, - {"color": "#D55E00", "name": "Bass"}, - {"color": "#CC79A7", "name": "Other"}, - {"color": "#56B4E9", "name": "Vocals"} - ] - ) - ) - - # lets write as multistream opus (supports only 48000 khz) - stempeg.write_stems( - "test.stem.opus", - stems, - sample_rate=96000, - writer=stempeg.StreamsWriter( - output_sample_rate=48000, - codec="opus" - ) - ) - - # writing to wav requires to convert streams to multichannel - stempeg.write_stems( - "test.wav", - stems, - sample_rate=96000, - writer=stempeg.ChannelsWriter( - output_sample_rate=48000 - ) - ) - - # # stempeg also supports to load merged-multichannel streams using - stems, rate = stempeg.read_stems( - "test.wav", - reader=stempeg.ChannelsReader(nb_channels=2) - ) - - # mp3 does not support multiple channels, - # therefore we have to use `stempeg.FilesWriter` - # outputs are named ["output/0.mp3", "output/1.mp3"] - # for named files, provide a dict or use `stem_names` - # also apply multiprocessing - stempeg.write_stems( - ("output", ".mp3"), + ("output", ".flac"), stems, sample_rate=rate, writer=stempeg.FilesWriter( - multiprocess=True, - output_sample_rate=48000, + output_sample_rate=44100, stem_names=["mix", "drums", "bass", "other", "vocals"] ) ) diff --git a/stempeg/__init__.py b/stempeg/__init__.py index b844125..7636807 100644 --- a/stempeg/__init__.py +++ b/stempeg/__init__.py @@ -20,7 +20,7 @@ from .write import write_audio from .write import FilesWriter, StreamsWriter, ChannelsWriter, NIStemsWriter -from .cmds import check_available_aac_encoders +from .cmds import check_available_aac_encoders, ffmpeg_exists, ffprobe_exists, mp4box_exists import re import os diff --git a/stempeg/cmds.py b/stempeg/cmds.py index ec17175..99c975c 100644 --- a/stempeg/cmds.py +++ b/stempeg/cmds.py @@ -1,6 +1,7 @@ import re import subprocess as sp import logging +import os FFMPEG_PATH = None FFPROBE_PATH = None @@ -20,30 +21,53 @@ def find_cmd(cmd): return None -def ffmpeg_and_ffprobe_exists(): - global FFMPEG_PATH, FFPROBE_PATH - if FFMPEG_PATH is None: +def ffmpeg_exists(): + global FFMPEG_PATH + # check environment variable + if "FFMPEG_PATH" in os.environ: + env_path = os.environ["FFMPEG_PATH"] + FFMPEG_PATH = find_cmd(env_path) + else: FFMPEG_PATH = find_cmd("ffmpeg") - if FFPROBE_PATH is None: + return FFMPEG_PATH is not None + + +def ffprobe_exists(): + global FFPROBE_PATH + if "FFPROBE_PATH" in os.environ: + env_path = os.environ["FFPROBE_PATH"] + FFPROBE_PATH = find_cmd(env_path) + else: FFPROBE_PATH = find_cmd("ffprobe") - return FFMPEG_PATH is not None and FFPROBE_PATH is not None + return FFPROBE_PATH is not None def mp4box_exists(): global MP4BOX_PATH - if MP4BOX_PATH is None: + if "MP4BOX_PATH" in os.environ: + env_path = os.environ["MP4BOX_PATH"] + MP4BOX_PATH = find_cmd(env_path) + else: MP4BOX_PATH = find_cmd("MP4Box") return MP4BOX_PATH is not None -if not ffmpeg_and_ffprobe_exists(): +if not ffmpeg_exists(): + raise RuntimeError( + "ffmpeg could not be found! " + "Please install it before using stempeg. " + "See: https://github.com/faroit/stempeg" + ) + + +if not ffprobe_exists(): raise RuntimeError( - 'ffmpeg or ffprobe could not be found! ' - 'Please install them before using stempeg. ' - 'See: https://github.com/faroit/stempeg' + "ffprobe could not be found! " + "Please install it before using stempeg. " + "See: https://github.com/faroit/stempeg" ) diff --git a/tests/test_read.py b/tests/test_read.py index 5979337..ca3df15 100644 --- a/tests/test_read.py +++ b/tests/test_read.py @@ -1,6 +1,7 @@ import stempeg import numpy as np import pytest +import os @pytest.fixture(params=[np.float16, np.float32, np.float64]) @@ -104,3 +105,15 @@ def test_info(): fp = stempeg.example_stem_path() info = stempeg.Info(fp) S, rate = stempeg.read_stems(fp, info=info) + + +def test_cmd(): + assert stempeg.ffmpeg_exists() + assert stempeg.mp4box_exists() + assert stempeg.ffprobe_exists() + os.environ["FFMPEG_PATH"] = "/path_that_does_not_exist" + os.environ["FFPROBE_PATH"] = "/path_that_does_not_exist" + os.environ["MP4BOX_PATH"] = "/path_that_does_not_exist" + assert not stempeg.ffprobe_exists() + assert not stempeg.ffmpeg_exists() + assert not stempeg.mp4box_exists() From 017aff8e7dca8c31e9c50a72830790abd68ee50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian-Robert=20St=C3=B6ter?= Date: Mon, 13 May 2024 09:37:09 +0200 Subject: [PATCH 2/7] add more ffmpeg version --- .github/workflows/test_unittests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_unittests.yml b/.github/workflows/test_unittests.yml index 2104f87..6d68f8d 100644 --- a/.github/workflows/test_unittests.yml +++ b/.github/workflows/test_unittests.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ffmpeg-version: ["3.2.4", "3.4", "4.0.2", "4.1", "4.2", "4.3"] + ffmpeg-version: ["3.2.4", "3.4.2", "4.4.2", "5.1.2", "6.1.1", "7.0.0"] # Timeout: https://stackoverflow.com/a/59076067/4521646 timeout-minutes: 10 From b830efc24e2d513e9328a8778dd200cad96492d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian-Robert=20St=C3=B6ter?= Date: Mon, 13 May 2024 09:46:18 +0200 Subject: [PATCH 3/7] echo --- .github/workflows/test_unittests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_unittests.yml b/.github/workflows/test_unittests.yml index 6d68f8d..a4fd83b 100644 --- a/.github/workflows/test_unittests.yml +++ b/.github/workflows/test_unittests.yml @@ -31,6 +31,7 @@ jobs: FFMPEG_INSTALL: ${{ matrix.pytorch-version }} run: | sudo apt-get -y install gpac + which MP4Box conda install -c conda-forge ffmpeg==${{ matrix.ffmpeg-version }} python -m pip install -e .['tests'] python --version From 234c6f5614fdd091a8d0ddb3acdc4f0724028fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian-Robert=20St=C3=B6ter?= Date: Mon, 13 May 2024 09:52:50 +0200 Subject: [PATCH 4/7] debug --- stempeg/cmds.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stempeg/cmds.py b/stempeg/cmds.py index 99c975c..aac9b9e 100644 --- a/stempeg/cmds.py +++ b/stempeg/cmds.py @@ -46,11 +46,14 @@ def ffprobe_exists(): def mp4box_exists(): global MP4BOX_PATH + print(MP4BOX_PATH) + if "MP4BOX_PATH" in os.environ: env_path = os.environ["MP4BOX_PATH"] MP4BOX_PATH = find_cmd(env_path) else: MP4BOX_PATH = find_cmd("MP4Box") + print(MP4BOX_PATH) return MP4BOX_PATH is not None From 3275ca0ce30f7a674ebf7f95ba517be0b6dda227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian-Robert=20St=C3=B6ter?= Date: Mon, 13 May 2024 09:58:54 +0200 Subject: [PATCH 5/7] force MP4BOX_PATH --- .github/workflows/test_unittests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_unittests.yml b/.github/workflows/test_unittests.yml index a4fd83b..9e40200 100644 --- a/.github/workflows/test_unittests.yml +++ b/.github/workflows/test_unittests.yml @@ -29,6 +29,7 @@ jobs: - name: Install dependencies FFMPEG ${{ matrix.ffmpeg-version }} env: FFMPEG_INSTALL: ${{ matrix.pytorch-version }} + MP4BOX_PATH: /usr/bin/MP4Box run: | sudo apt-get -y install gpac which MP4Box From f3fd8ef04788fd118f5dedd3593e26e1beaa023b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian-Robert=20St=C3=B6ter?= Date: Mon, 13 May 2024 10:03:50 +0200 Subject: [PATCH 6/7] update write --- stempeg/write.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stempeg/write.py b/stempeg/write.py index d0b7880..fd28896 100644 --- a/stempeg/write.py +++ b/stempeg/write.py @@ -20,7 +20,7 @@ import stempeg -from .cmds import FFMPEG_PATH, mp4box_exists, get_aac_codec, find_cmd +from .cmds import FFMPEG_PATH, MP4BOX_PATH, mp4box_exists, get_aac_codec, find_cmd def _build_channel_map(nb_stems, nb_channels, stem_names=None): @@ -497,7 +497,6 @@ def __init__( 'Please install them before using NIStemsWriter().' 'See: https://github.com/faroit/stempeg' ) - self.mp4boxcli = find_cmd("MP4Box") self.bitrate = bitrate self.default_metadata = default_metadata self.stems_metadata = stems_metadata @@ -564,7 +563,7 @@ def __call__( if self.stems_metadata is not None: metadata['stems'] = self.stems_metadata - callArgs = [self.mp4boxcli] + callArgs = [MP4BOX_PATH] callArgs.extend(["-add", str(Path(tempdir, '0.m4a#ID=Z')), path]) for s in range(1, data.shape[0]): callArgs.extend( @@ -758,7 +757,7 @@ def write_stems( """ # check if ffmpeg installed if int(stempeg.ffmpeg_version()[0]) < 3: - warnings.warning( + warnings.warn( "Writing stems with FFMPEG version < 3 is unsupported", UserWarning ) From 95a533f9381c0375fb825615f311d2fc18f59559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian-Robert=20St=C3=B6ter?= Date: Mon, 13 May 2024 10:19:50 +0200 Subject: [PATCH 7/7] check task --- tests/test_write.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_write.py b/tests/test_write.py index a645386..5e0d369 100644 --- a/tests/test_write.py +++ b/tests/test_write.py @@ -1,4 +1,5 @@ from stempeg.write import ChannelsWriter +from stempeg.cmds import MP4BOX_PATH import stempeg import numpy as np import pytest @@ -161,8 +162,6 @@ def ordered(obj): def test_nistems(): - mp4exc = stempeg.cmds.find_cmd("MP4Box") - stems, rate = stempeg.read_stems(stempeg.example_stem_path()) with tmp.NamedTemporaryFile( delete=False, @@ -175,7 +174,7 @@ def test_nistems(): sample_rate=rate, writer=stempeg.NIStemsWriter() ) - callArgs = [mp4exc] + callArgs = [MP4BOX_PATH] callArgs.extend(["-dump-udta", "0:stem", tempfile.name]) sp.check_call(callArgs)