Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pymusiclooper/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,10 @@ def split_audio(**kwargs):
@common_loop_options
@common_export_options
@click.option('--format', type=click.Choice(("WAV", "FLAC", "OGG", "MP3"), case_sensitive=False), default="MP3", show_default=True, help="Audio format to use for the output audio file.")
@click.option('--extended-length', type=float, required=True, help="Desired length of the extended looped track in seconds. [Must be longer than the audio's original length.]")
@click.option('--extended-length', type=float, required=False, help="Desired length of the extended looped track in seconds. [Must be longer than the audio's original length.] [dim cyan]\[mutually exclusive with --extended-count][/] [dim red]\[at least one required][/]")
@click.option('--extended-count', type=float, required=False, help="Desired number of loops. [Must be more than 1.] [dim cyan]\[mutually exclusive with --extended-length][/] [dim red]\[at least one required][/]")
@click.option('--fade-length', type=float, default=5, show_default=True, help="Desired length of the loop fade out in seconds.")
@click.option('--disable-fade-out', is_flag=True, default=False, help="Extend the track with all its sections (intro/loop/outro) without fading out. --extended-length will be treated as an 'at least' constraint.")
@click.option('--disable-fade-out', is_flag=True, default=False, help="Extend the track with all its sections (intro/loop/outro) without fading out. --extended-length and --extended-count will be treated as an 'at least' constraint.")
def extend(**kwargs):
"""Create an extended version of the input audio by looping it to a specific length."""
run_handler(**kwargs)
Expand Down
2 changes: 1 addition & 1 deletion pymusiclooper/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def _option_groups(additional_basic_options=None):
"pymusiclooper split-audio": _common_option_groups,
"pymusiclooper tag": _option_groups(["--tag-names", "--tag-offset"]),
"pymusiclooper export-points": _option_groups(["--export-to", "--alt-export-top", "--fmt"]),
"pymusiclooper extend": _option_groups(["--extended-length", "--fade-length", "--disable-fade-out"]),
"pymusiclooper extend": _option_groups(["--extended-length", "--extended-count", "--fade-length", "--disable-fade-out"]),
}
_COMMAND_GROUPS = {
"pymusiclooper": [
Expand Down
59 changes: 35 additions & 24 deletions pymusiclooper/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ def extend(
self,
loop_start: int,
loop_end: int,
extended_length: float,
extended_length: Optional[float],
extended_count: Optional[float],
fade_length: float = 5,
disable_fade_out: bool = False,
format: str = "WAV",
Expand All @@ -177,16 +178,21 @@ def extend(
else:
out_path = os.path.abspath(self.mlaudio.filepath)

if extended_length < self.mlaudio.total_duration:
raise ValueError(
"Extended length must be greater than the audio's original length."
)

intro = self.mlaudio.playback_audio[:loop_start]
loop = self.mlaudio.playback_audio[loop_start:loop_end]
outro = self.mlaudio.playback_audio[loop_end:]

loop_extended_length = self.mlaudio.seconds_to_samples(extended_length) - intro.shape[0]
if extended_length is not None and extended_length < self.mlaudio.total_duration:
raise ValueError(
"Extended length must be greater than the audio's original length."
)

if extended_count is not None:
if extended_length is not None:
raise ValueError("Must not specify extended length and extended count simultaneously.")
loop_extended_length = round(extended_count * loop.shape[0])
else:
loop_extended_length = self.mlaudio.seconds_to_samples(extended_length) - intro.shape[0]

# If the outro will be included, account for its length when calculating the new loop duration
if disable_fade_out:
Expand All @@ -212,23 +218,28 @@ def extend(
)

# Format extended file name with its duration suffixed
extended_loop_length = final_loop.shape[0] + (
loop.shape[0] * (int(loop_factor))
)
extended_audio_length = (
intro.shape[0]
+ extended_loop_length
+ (outro.shape[0] if disable_fade_out else 0)
)
total_length_seconds = self.mlaudio.samples_to_seconds(extended_audio_length)
duration_sec = ceil(total_length_seconds%60)
duration_mins = int(total_length_seconds//60)
if duration_sec == 60:
duration_sec = 0
duration_mins += 1
extended_audio_length_fmt = (
f"{duration_mins:d}m{duration_sec:02d}s"
)
if extended_length is not None:
extended_loop_length = final_loop.shape[0] + (
loop.shape[0] * (int(loop_factor))
)
extended_audio_length = (
intro.shape[0]
+ extended_loop_length
+ (outro.shape[0] if disable_fade_out else 0)
)
total_length_seconds = self.mlaudio.samples_to_seconds(extended_audio_length)
duration_sec = ceil(total_length_seconds%60)
duration_mins = int(total_length_seconds//60)
if duration_sec == 60:
duration_sec = 0
duration_mins += 1
extended_audio_length_fmt = (
f"{duration_mins:d}m{duration_sec:02d}s"
)
else:
extended_audio_length_fmt = (
f"{extended_count:g}lp"
)
output_file_path = (
f"{out_path}-extended-{extended_audio_length_fmt}.{format.lower()}"
)
Expand Down
10 changes: 8 additions & 2 deletions pymusiclooper/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ def __init__(
tag_names: Optional[Tuple[str, str]] = None,
tag_offset: Optional[bool] = None,
batch_mode: bool = False,
extended_length: float = 0,
extended_length: Optional[float] = None,
extended_count: Optional[float] = None,
fade_length: float = 0,
disable_fade_out: bool = False,
**kwargs,
Expand All @@ -222,7 +223,10 @@ def __init__(
self.tag_names = tag_names
self.tag_offset = tag_offset
self.batch_mode = batch_mode
if (extended_length is not None) and (extended_count is not None):
raise ValueError("Must not specify --extended-length and --extended_count simultaneously")
self.extended_length = extended_length
self.extended_count = extended_count
self.disable_fade_out = disable_fade_out
self.fade_length = fade_length
self._is_autocreated_outdir = False
Expand All @@ -245,6 +249,7 @@ def run(self):
or self.to_txt
or self.split_audio
or self.extended_length
or self.extended_count is not None
) and not os.path.exists(self.output_directory):
os.mkdir(self.output_directory)
self._is_autocreated_outdir = True
Expand All @@ -258,7 +263,7 @@ def run(self):
if self.split_audio:
self.split_audio_runner(loop_start, loop_end)

if self.extended_length:
if self.extended_length or self.extended_count is not None:
self.extend_track_runner(loop_start, loop_end)
finally:
if (
Expand Down Expand Up @@ -305,6 +310,7 @@ def extend_track_runner(self, loop_start: int, loop_end: int):
format=self.format,
output_dir=self.output_directory,
extended_length=self.extended_length,
extended_count=self.extended_count,
disable_fade_out=self.disable_fade_out,
fade_length=self.fade_length,
)
Expand Down