videopython 0.26.4__tar.gz → 0.26.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {videopython-0.26.4 → videopython-0.26.5}/PKG-INFO +1 -1
- {videopython-0.26.4 → videopython-0.26.5}/pyproject.toml +1 -1
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/dubbing/dubber.py +23 -3
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/dubbing/pipeline.py +64 -19
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/understanding/separation.py +27 -40
- {videopython-0.26.4 → videopython-0.26.5}/.gitignore +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/LICENSE +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/README.md +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/_device.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/dubbing/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/dubbing/models.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/dubbing/remux.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/dubbing/timing.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/generation/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/generation/audio.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/generation/image.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/generation/translation.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/generation/video.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/registry.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/swapping/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/swapping/inpainter.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/swapping/models.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/swapping/segmenter.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/swapping/swapper.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/transforms.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/understanding/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/understanding/audio.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/understanding/image.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/understanding/temporal.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/ai/video_analysis.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/audio/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/audio/analysis.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/audio/audio.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/combine.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/description.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/effects.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/exceptions.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/progress.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/registry.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/scene.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/streaming.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/text/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/text/overlay.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/text/transcription.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/transforms.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/transitions.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/utils.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/base/video.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/editing/__init__.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/editing/multicam.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/editing/premiere_xml.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/editing/video_edit.py +0 -0
- {videopython-0.26.4 → videopython-0.26.5}/src/videopython/py.typed +0 -0
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from typing import TYPE_CHECKING, Any, Callable
|
|
9
9
|
|
|
10
10
|
from videopython.ai.dubbing.models import DubbingResult, RevoiceResult
|
|
11
|
+
from videopython.ai.dubbing.pipeline import WhisperModel
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
13
14
|
from videopython.base.video import Video
|
|
@@ -25,19 +26,38 @@ class VideoDubber:
|
|
|
25
26
|
model is resident at a time. Trades per-run latency (~10-30s of
|
|
26
27
|
extra model loads) for a much lower memory ceiling. Recommended for
|
|
27
28
|
GPUs with <=12GB VRAM or hosts with <32GB RAM. Default False.
|
|
29
|
+
whisper_model: Whisper model size used for transcription. Larger models
|
|
30
|
+
give better accuracy at the cost of VRAM and latency. One of
|
|
31
|
+
``tiny``, ``base``, ``small``, ``medium``, ``large``, ``turbo``.
|
|
32
|
+
Default ``small``.
|
|
28
33
|
"""
|
|
29
34
|
|
|
30
|
-
def __init__(
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
device: str | None = None,
|
|
38
|
+
low_memory: bool = False,
|
|
39
|
+
whisper_model: WhisperModel = "small",
|
|
40
|
+
):
|
|
31
41
|
self.device = device
|
|
32
42
|
self.low_memory = low_memory
|
|
43
|
+
self.whisper_model = whisper_model
|
|
33
44
|
self._local_pipeline: Any = None
|
|
34
45
|
requested = device.lower() if isinstance(device, str) else "auto"
|
|
35
|
-
logger.info(
|
|
46
|
+
logger.info(
|
|
47
|
+
"VideoDubber initialized with device=%s low_memory=%s whisper_model=%s",
|
|
48
|
+
requested,
|
|
49
|
+
low_memory,
|
|
50
|
+
whisper_model,
|
|
51
|
+
)
|
|
36
52
|
|
|
37
53
|
def _init_local_pipeline(self) -> None:
|
|
38
54
|
from videopython.ai.dubbing.pipeline import LocalDubbingPipeline
|
|
39
55
|
|
|
40
|
-
self._local_pipeline = LocalDubbingPipeline(
|
|
56
|
+
self._local_pipeline = LocalDubbingPipeline(
|
|
57
|
+
device=self.device,
|
|
58
|
+
low_memory=self.low_memory,
|
|
59
|
+
whisper_model=self.whisper_model,
|
|
60
|
+
)
|
|
41
61
|
|
|
42
62
|
def dub(
|
|
43
63
|
self,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Callable
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Callable, Literal
|
|
7
7
|
|
|
8
8
|
from videopython.ai.dubbing.models import DubbingResult, RevoiceResult, SeparatedAudio
|
|
9
9
|
from videopython.ai.dubbing.timing import TimingSynchronizer
|
|
@@ -11,6 +11,8 @@ from videopython.ai.dubbing.timing import TimingSynchronizer
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from videopython.base.audio import Audio
|
|
13
13
|
|
|
14
|
+
WhisperModel = Literal["tiny", "base", "small", "medium", "large", "turbo"]
|
|
15
|
+
|
|
14
16
|
logger = logging.getLogger(__name__)
|
|
15
17
|
|
|
16
18
|
|
|
@@ -23,14 +25,21 @@ class LocalDubbingPipeline:
|
|
|
23
25
|
with <=12GB VRAM or hosts with <32GB RAM.
|
|
24
26
|
"""
|
|
25
27
|
|
|
26
|
-
def __init__(
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
device: str | None = None,
|
|
31
|
+
low_memory: bool = False,
|
|
32
|
+
whisper_model: WhisperModel = "small",
|
|
33
|
+
):
|
|
27
34
|
self.device = device
|
|
28
35
|
self.low_memory = low_memory
|
|
36
|
+
self.whisper_model = whisper_model
|
|
29
37
|
requested = device.lower() if isinstance(device, str) else "auto"
|
|
30
38
|
logger.info(
|
|
31
|
-
"LocalDubbingPipeline initialized with device=%s low_memory=%s",
|
|
39
|
+
"LocalDubbingPipeline initialized with device=%s low_memory=%s whisper_model=%s",
|
|
32
40
|
requested,
|
|
33
41
|
low_memory,
|
|
42
|
+
whisper_model,
|
|
34
43
|
)
|
|
35
44
|
|
|
36
45
|
self._transcriber: Any = None
|
|
@@ -62,7 +71,11 @@ class LocalDubbingPipeline:
|
|
|
62
71
|
"""Initialize the transcription model."""
|
|
63
72
|
from videopython.ai.understanding.audio import AudioToText
|
|
64
73
|
|
|
65
|
-
self._transcriber = AudioToText(
|
|
74
|
+
self._transcriber = AudioToText(
|
|
75
|
+
model_name=self.whisper_model,
|
|
76
|
+
device=self.device,
|
|
77
|
+
enable_diarization=enable_diarization,
|
|
78
|
+
)
|
|
66
79
|
|
|
67
80
|
def _init_translator(self) -> None:
|
|
68
81
|
"""Initialize the translation model."""
|
|
@@ -94,6 +107,7 @@ class LocalDubbingPipeline:
|
|
|
94
107
|
max_duration: float = 10.0,
|
|
95
108
|
) -> dict[str, Any]:
|
|
96
109
|
"""Extract voice samples for each speaker from the audio."""
|
|
110
|
+
from videopython.base.audio import Audio
|
|
97
111
|
|
|
98
112
|
voice_samples: dict[str, Audio] = {}
|
|
99
113
|
|
|
@@ -120,7 +134,11 @@ class LocalDubbingPipeline:
|
|
|
120
134
|
if best_segment is not None:
|
|
121
135
|
start = best_segment.start
|
|
122
136
|
end = min(best_segment.end, start + max_duration)
|
|
123
|
-
|
|
137
|
+
sliced = audio.slice(start, end)
|
|
138
|
+
# Audio.slice returns a numpy view into the source. Copy so the
|
|
139
|
+
# short voice sample doesn't keep the full vocals array (~1.3 GB
|
|
140
|
+
# for 2h sources) alive across translate + TTS.
|
|
141
|
+
voice_samples[speaker] = Audio(sliced.data.copy(), sliced.metadata)
|
|
124
142
|
|
|
125
143
|
return voice_samples
|
|
126
144
|
|
|
@@ -175,6 +193,7 @@ class LocalDubbingPipeline:
|
|
|
175
193
|
|
|
176
194
|
separated_audio: SeparatedAudio | None = None
|
|
177
195
|
vocal_audio = source_audio
|
|
196
|
+
background_audio: Audio | None = None
|
|
178
197
|
|
|
179
198
|
if preserve_background:
|
|
180
199
|
report_progress("Separating audio", 0.15)
|
|
@@ -184,12 +203,24 @@ class LocalDubbingPipeline:
|
|
|
184
203
|
separated_audio = self._separator.separate(source_audio)
|
|
185
204
|
self._maybe_unload("_separator")
|
|
186
205
|
vocal_audio = separated_audio.vocals
|
|
206
|
+
background_audio = separated_audio.background
|
|
207
|
+
# In low_memory mode, drop the SeparatedAudio container so vocals
|
|
208
|
+
# and background can be released as soon as their last local
|
|
209
|
+
# reference goes (after voice-sample extraction and final overlay
|
|
210
|
+
# respectively). The result will report separated_audio=None.
|
|
211
|
+
if self.low_memory:
|
|
212
|
+
separated_audio = None
|
|
187
213
|
|
|
188
214
|
voice_samples: dict[str, Audio] = {}
|
|
189
215
|
if voice_clone:
|
|
190
216
|
report_progress("Extracting voice samples", 0.25)
|
|
191
217
|
voice_samples = self._extract_voice_samples(vocal_audio, transcription)
|
|
192
218
|
|
|
219
|
+
# vocals is no longer needed; voice_samples are independent copies.
|
|
220
|
+
# In low_memory mode this is the only ref keeping the buffer alive
|
|
221
|
+
# (separated_audio was dropped above), so dropping the local frees it.
|
|
222
|
+
del vocal_audio
|
|
223
|
+
|
|
193
224
|
report_progress("Translating text", 0.35)
|
|
194
225
|
if self._translator is None:
|
|
195
226
|
self._init_translator()
|
|
@@ -237,17 +268,23 @@ class LocalDubbingPipeline:
|
|
|
237
268
|
assert self._synchronizer is not None
|
|
238
269
|
|
|
239
270
|
synchronized_segments, _ = self._synchronizer.synchronize_segments(dubbed_segments, target_durations)
|
|
271
|
+
del dubbed_segments
|
|
240
272
|
|
|
241
273
|
report_progress("Assembling final audio", 0.90)
|
|
242
274
|
total_duration = source_audio.metadata.duration_seconds
|
|
243
275
|
dubbed_speech = self._synchronizer.assemble_with_timing(synchronized_segments, start_times, total_duration)
|
|
276
|
+
del synchronized_segments
|
|
244
277
|
|
|
245
|
-
if
|
|
246
|
-
background_sr =
|
|
278
|
+
if background_audio is not None:
|
|
279
|
+
background_sr = background_audio.metadata.sample_rate
|
|
247
280
|
if dubbed_speech.metadata.sample_rate != background_sr:
|
|
248
281
|
dubbed_speech = dubbed_speech.resample(background_sr)
|
|
249
282
|
|
|
250
|
-
final_audio =
|
|
283
|
+
final_audio = background_audio.overlay(dubbed_speech, position=0.0)
|
|
284
|
+
# Drop the local; in low_memory this releases the background
|
|
285
|
+
# buffer (~1.3 GB for 2h sources). In non-low_memory the same
|
|
286
|
+
# array is still held by separated_audio.background.
|
|
287
|
+
del background_audio
|
|
251
288
|
else:
|
|
252
289
|
final_audio = dubbed_speech
|
|
253
290
|
|
|
@@ -294,6 +331,7 @@ class LocalDubbingPipeline:
|
|
|
294
331
|
|
|
295
332
|
separated_audio: SeparatedAudio | None = None
|
|
296
333
|
vocal_audio = source_audio
|
|
334
|
+
background_audio: Audio | None = None
|
|
297
335
|
|
|
298
336
|
if preserve_background:
|
|
299
337
|
report_progress("Separating audio", 0.20)
|
|
@@ -303,6 +341,9 @@ class LocalDubbingPipeline:
|
|
|
303
341
|
separated_audio = self._separator.separate(source_audio)
|
|
304
342
|
self._maybe_unload("_separator")
|
|
305
343
|
vocal_audio = separated_audio.vocals
|
|
344
|
+
background_audio = separated_audio.background
|
|
345
|
+
if self.low_memory:
|
|
346
|
+
separated_audio = None
|
|
306
347
|
|
|
307
348
|
report_progress("Extracting voice sample", 0.40)
|
|
308
349
|
voice_sample: Audio | None = None
|
|
@@ -314,7 +355,11 @@ class LocalDubbingPipeline:
|
|
|
314
355
|
|
|
315
356
|
if voice_sample is None:
|
|
316
357
|
sample_duration = min(6.0, original_duration)
|
|
317
|
-
|
|
358
|
+
sliced = vocal_audio.slice(0, sample_duration)
|
|
359
|
+
# Copy so the short sample doesn't pin the full vocals array.
|
|
360
|
+
voice_sample = Audio(sliced.data.copy(), sliced.metadata)
|
|
361
|
+
|
|
362
|
+
del vocal_audio
|
|
318
363
|
|
|
319
364
|
report_progress("Generating speech", 0.60)
|
|
320
365
|
if self._tts is None or self._tts_language != "en":
|
|
@@ -327,24 +372,24 @@ class LocalDubbingPipeline:
|
|
|
327
372
|
|
|
328
373
|
report_progress("Assembling audio", 0.85)
|
|
329
374
|
|
|
330
|
-
if
|
|
331
|
-
background_sr =
|
|
375
|
+
if background_audio is not None:
|
|
376
|
+
background_sr = background_audio.metadata.sample_rate
|
|
332
377
|
if generated_speech.metadata.sample_rate != background_sr:
|
|
333
378
|
generated_speech = generated_speech.resample(background_sr)
|
|
334
379
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
silence_duration = speech_duration - background.metadata.duration_seconds
|
|
380
|
+
if background_audio.metadata.duration_seconds > speech_duration:
|
|
381
|
+
background_audio = background_audio.slice(0, speech_duration)
|
|
382
|
+
elif background_audio.metadata.duration_seconds < speech_duration:
|
|
383
|
+
silence_duration = speech_duration - background_audio.metadata.duration_seconds
|
|
340
384
|
silence = Audio.silence(
|
|
341
385
|
duration=silence_duration,
|
|
342
386
|
sample_rate=background_sr,
|
|
343
|
-
channels=
|
|
387
|
+
channels=background_audio.metadata.channels,
|
|
344
388
|
)
|
|
345
|
-
|
|
389
|
+
background_audio = background_audio.concat(silence)
|
|
346
390
|
|
|
347
|
-
final_audio =
|
|
391
|
+
final_audio = background_audio.overlay(generated_speech, position=0.0)
|
|
392
|
+
del background_audio
|
|
348
393
|
else:
|
|
349
394
|
final_audio = generated_speech
|
|
350
395
|
|
|
@@ -42,7 +42,15 @@ class AudioSeparator:
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
def _separate_local(self, audio: Audio) -> SeparatedAudio:
|
|
45
|
-
"""Separate audio using local Demucs model.
|
|
45
|
+
"""Separate audio using local Demucs model.
|
|
46
|
+
|
|
47
|
+
Keeps the input tensor on CPU and passes ``device=self.device`` to
|
|
48
|
+
``apply_model`` so per-chunk compute runs on GPU while the full
|
|
49
|
+
``(stems, channels, samples)`` output is stored in CPU RAM. For long
|
|
50
|
+
sources this is the difference between OOM-on-GPU and running cleanly:
|
|
51
|
+
a 2h stereo @ 44.1kHz output is ~10 GB — too big for an 8 GB card but
|
|
52
|
+
comfortable on a 32 GB host.
|
|
53
|
+
"""
|
|
46
54
|
import numpy as np
|
|
47
55
|
import torch
|
|
48
56
|
from demucs.apply import apply_model
|
|
@@ -65,61 +73,40 @@ class AudioSeparator:
|
|
|
65
73
|
audio_data = audio_data.T
|
|
66
74
|
|
|
67
75
|
wav = torch.tensor(audio_data, dtype=torch.float32).unsqueeze(0)
|
|
68
|
-
wav = wav.to(self.device)
|
|
69
76
|
|
|
70
77
|
with torch.no_grad():
|
|
71
78
|
sources = apply_model(self._model, wav, device=self.device)
|
|
72
79
|
|
|
73
80
|
sources_np = sources[0].cpu().numpy()
|
|
81
|
+
del sources
|
|
74
82
|
|
|
75
83
|
stem_names = self.STEM_NAMES_6S if self.model_name == "htdemucs_6s" else self.STEM_NAMES
|
|
84
|
+
vocals_idx = stem_names.index("vocals")
|
|
85
|
+
non_vocal_indices = [i for i in range(len(stem_names)) if i != vocals_idx]
|
|
76
86
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
metadata = AudioMetadata(
|
|
82
|
-
sample_rate=target_sr,
|
|
83
|
-
channels=2,
|
|
84
|
-
sample_width=2,
|
|
85
|
-
duration_seconds=stem_data.shape[0] / target_sr,
|
|
86
|
-
frame_count=stem_data.shape[0],
|
|
87
|
-
)
|
|
88
|
-
stems[name] = Audio(stem_data.astype(np.float32), metadata)
|
|
89
|
-
|
|
90
|
-
vocals = stems["vocals"]
|
|
91
|
-
|
|
92
|
-
non_vocal_stems = [stems[name] for name in stem_names if name != "vocals"]
|
|
93
|
-
background_data = np.zeros_like(vocals.data)
|
|
94
|
-
for stem in non_vocal_stems:
|
|
95
|
-
background_data += stem.data
|
|
87
|
+
vocals_data = sources_np[vocals_idx].T
|
|
88
|
+
background_data = sources_np[non_vocal_indices].sum(axis=0).T
|
|
89
|
+
del sources_np
|
|
96
90
|
|
|
97
91
|
max_val = np.max(np.abs(background_data))
|
|
98
92
|
if max_val > 1.0:
|
|
99
|
-
background_data
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
music_data += stems[name].data
|
|
111
|
-
|
|
112
|
-
max_val = np.max(np.abs(music_data))
|
|
113
|
-
if max_val > 1.0:
|
|
114
|
-
music_data = music_data / max_val
|
|
115
|
-
|
|
116
|
-
music = Audio(music_data.astype(np.float32), vocals.metadata)
|
|
93
|
+
background_data /= max_val
|
|
94
|
+
|
|
95
|
+
metadata = AudioMetadata(
|
|
96
|
+
sample_rate=target_sr,
|
|
97
|
+
channels=2,
|
|
98
|
+
sample_width=2,
|
|
99
|
+
duration_seconds=vocals_data.shape[0] / target_sr,
|
|
100
|
+
frame_count=vocals_data.shape[0],
|
|
101
|
+
)
|
|
102
|
+
vocals = Audio(np.ascontiguousarray(vocals_data, dtype=np.float32), metadata)
|
|
103
|
+
background = Audio(np.ascontiguousarray(background_data, dtype=np.float32), metadata)
|
|
117
104
|
|
|
118
105
|
return SeparatedAudio(
|
|
119
106
|
vocals=vocals,
|
|
120
107
|
background=background,
|
|
121
108
|
original=audio,
|
|
122
|
-
music=
|
|
109
|
+
music=None,
|
|
123
110
|
effects=None,
|
|
124
111
|
)
|
|
125
112
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|