Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: arkrow/PyMusicLooper
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.4.2
Choose a base ref
...
head repository: arkrow/PyMusicLooper
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 1 commit
  • 4 files changed
  • 1 contributor

Commits on Nov 30, 2024

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4b2603c View commit details
Showing with 39 additions and 7 deletions.
  1. +5 −3 pymusiclooper/cli.py
  2. +1 −1 pymusiclooper/console.py
  3. +29 −2 pymusiclooper/core.py
  4. +4 −1 pymusiclooper/handler.py
8 changes: 5 additions & 3 deletions pymusiclooper/cli.py
Original file line number Diff line number Diff line change
@@ -143,12 +143,13 @@ def play(**kwargs):

@cli_main.command()
@click.option('--path', type=click.Path(exists=True), required=True, help='Path to the audio file.')
@click.option("--tag-names", type=str, required=True, nargs=2, help="Name of the loop metadata tags to read from, e.g. --tags-names LOOP_START LOOP_END (note: values must be integers and in sample units).")
def play_tagged(path, tag_names):
@click.option("--tag-names", type=str, required=True, nargs=2, help="Name of the loop metadata tags to read from, e.g. --tag-names LOOP_START LOOP_END (note: values must be integers and in sample units).")
@click.option("--tag-offset/--no-tag-offset", is_flag=True, default=None, help="Always parse second loop metadata tag as a relative length / or as an absolute length. Default: auto-detected based on tag name.")
def play_tagged(path, tag_names, tag_offset):
"""Skips loop analysis and reads the loop points directly from the tags present in the file."""
try:
looper = MusicLooper(path)
loop_start, loop_end = looper.read_tags(tag_names[0], tag_names[1])
loop_start, loop_end = looper.read_tags(tag_names[0], tag_names[1], tag_offset)

in_samples = "PML_DISPLAY_SAMPLES" in os.environ

@@ -216,6 +217,7 @@ def export_points(**kwargs):
@common_loop_options
@common_export_options
@click.option('--tag-names', type=str, required=True, nargs=2, help='Name of the loop metadata tags to use, e.g. --tag-names LOOP_START LOOP_END')
@click.option("--tag-offset/--no-tag-offset", is_flag=True, default=None, help="Always export second loop metadata tag as a relative length / or as an absolute length. Default: auto-detected based on tag name.")
def tag(**kwargs):
"""Adds metadata tags of loop points to a copy of the input audio file(s)."""
run_handler(**kwargs)
2 changes: 1 addition & 1 deletion pymusiclooper/console.py
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ def _option_groups(additional_basic_options=None):
_OPTION_GROUPS = {
"pymusiclooper play": _common_option_groups,
"pymusiclooper split-audio": _common_option_groups,
"pymusiclooper tag": _option_groups(["--tag-names"]),
"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"]),
}
31 changes: 29 additions & 2 deletions pymusiclooper/core.py
Original file line number Diff line number Diff line change
@@ -274,21 +274,37 @@ def export_txt(
with open(out_path, "a") as file:
file.write(f"{loop_start} {loop_end} {self.mlaudio.filename}\n")


def _end_tag_is_offset(
self,
loop_end_tag: str,
is_offset: Optional[bool],
) -> bool:
if is_offset is not None:
return is_offset

upper_loop_end_tag = loop_end_tag.upper()

return "LEN" in upper_loop_end_tag or "OFFSET" in upper_loop_end_tag


def export_tags(
self,
loop_start: int,
loop_end: int,
loop_start_tag: str,
loop_end_tag: str,
is_offset: Optional[bool] = None,
output_dir: Optional[str] = None
):
) -> Tuple[str]:
"""Adds metadata tags of loop points to a copy of the source audio file.
Args:
loop_start (int): Loop start in samples.
loop_end (int): Loop end in samples.
loop_start_tag (str): Name of the loop_start metadata tag.
loop_end_tag (str): Name of the loop_end metadata tag.
is_offset (bool, optional): Export second tag as relative length / absolute end. Defaults to auto-detecting based on tag name.
output_dir (str, optional): Path to the output directory. Defaults to the same diretcory as the source audio file.
"""
# Workaround for taglib import issues on Apple silicon devices
@@ -305,17 +321,24 @@ def export_tags(
)
shutil.copyfile(self.mlaudio.filepath, exported_file_path)

# Handle LOOPLENGTH tag
if self._end_tag_is_offset(loop_end_tag, is_offset):
loop_end = loop_end - loop_start

with taglib.File(exported_file_path, save_on_exit=True) as audio_file:
audio_file.tags[loop_start_tag] = [str(loop_start)]
audio_file.tags[loop_end_tag] = [str(loop_end)]

return str(loop_start), str(loop_end)

def read_tags(self, loop_start_tag: str, loop_end_tag: str) -> Tuple[int, int]:

def read_tags(self, loop_start_tag: str, loop_end_tag: str, is_offset: Optional[bool] = None) -> Tuple[int, int]:
"""Reads the tags provided from the file and returns the read loop points
Args:
loop_start_tag (str): The name of the metadata tag containing the loop_start value
loop_end_tag (str): The name of the metadata tag containing the loop_end value
is_offset (bool, optional): Parse second tag as relative length / absolute end. Defaults to auto-detecting based on tag name.
Returns:
Tuple[int, int]: A tuple containing (loop_start, loop_end)
@@ -344,4 +367,8 @@ def read_tags(self, loop_start_tag: str, loop_end_tag: str) -> Tuple[int, int]:
real_loop_start = min(loop_start, loop_end)
real_loop_end = max(loop_start, loop_end)

# Handle LOOPLENGTH tag
if self._end_tag_is_offset(loop_end_tag, is_offset):
real_loop_end = real_loop_start + real_loop_end

return real_loop_start, real_loop_end
5 changes: 4 additions & 1 deletion pymusiclooper/handler.py
Original file line number Diff line number Diff line change
@@ -188,6 +188,7 @@ def __init__(
fmt: Literal["SAMPLES", "SECONDS", "TIME"] = "SAMPLES",
alt_export_top: int = 0,
tag_names: Optional[Tuple[str, str]] = None,
tag_offset: Optional[bool] = None,
batch_mode: bool = False,
extended_length: float = 0,
fade_length: float = 0,
@@ -211,6 +212,7 @@ def __init__(
self.fmt = fmt.lower()
self.alt_export_top = alt_export_top
self.tag_names = tag_names
self.tag_offset = tag_offset
self.batch_mode = batch_mode
self.extended_length = extended_length
self.disable_fade_out = disable_fade_out
@@ -341,11 +343,12 @@ def fmt_line(pair: LoopPair):

def tag_runner(self, loop_start: int, loop_end: int):
loop_start_tag, loop_end_tag = self.tag_names
self.musiclooper.export_tags(
loop_start, loop_end = self.musiclooper.export_tags(
loop_start,
loop_end,
loop_start_tag,
loop_end_tag,
is_offset=self.tag_offset,
output_dir=self.output_directory,
)
message = f"Exported {loop_start_tag}: {loop_start} and {loop_end_tag}: {loop_end} of \"{self.musiclooper.filename}\" to a copy in \"{self.output_directory}\""