Skip to content

Commit

Permalink
Encapsulate stats calculation inside result class
Browse files Browse the repository at this point in the history
  • Loading branch information
john-kurkowski committed Dec 13, 2024
1 parent 4466959 commit 4334349
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/music/codegen/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import openai

from music.render.process import _cmd_for_stats
from music.render.result import _cmd_for_stats


def main(example_audio_file: Path) -> None:
Expand Down
48 changes: 9 additions & 39 deletions src/music/render/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import rich.progress
import rich.table

from music.__codegen__ import stats
from music.util import (
ExtendedProject,
SongVersion,
Expand All @@ -36,33 +35,10 @@
mute_tracks,
toggle_fx_for_tracks,
)
from .result import RenderResult
from .result import ExistingRenderResult, RenderResult
from .tracks import find_acappella_tracks_to_mute, find_stems, find_vox_tracks_to_mute


def summary_stats_for_file(fil: Path, *, verbose: int = 0) -> dict[str, float | str]:
"""Print statistics for the given audio file, like LUFS-I and LRA."""
cmd = _cmd_for_stats(fil)
proc = subprocess.run(cmd, check=True, stderr=subprocess.PIPE, text=True)
proc_output = proc.stderr
return stats.parse_summary_stats(proc_output)


def _cmd_for_stats(fil: Path) -> list[str | Path]:
return [
"ffmpeg",
"-i",
fil,
"-filter:a",
",".join(("volumedetect", "ebur128=framelog=verbose")),
"-hide_banner",
"-nostats",
"-f",
"null",
"/dev/null",
]


async def render_version(
project: ExtendedProject, version: SongVersion
) -> RenderResult:
Expand Down Expand Up @@ -98,7 +74,9 @@ async def render_version(
rm_rf(out_fil)
shutil.move(tmp_fil, out_fil)

return RenderResult(out_fil, datetime.timedelta(seconds=time_end - time_start))
return RenderResult(
project, version, out_fil, datetime.timedelta(seconds=time_end - time_start)
)


def trim_silence(fil: Path) -> None:
Expand Down Expand Up @@ -303,7 +281,7 @@ async def process(
yield (
version,
await self._print_stats_for_render(
project, version, render, verbose=verbose
ExistingRenderResult(project, version), render, verbose=verbose
),
)

Expand Down Expand Up @@ -333,8 +311,7 @@ def _add_task(

async def _print_stats_for_render(
self,
project: ExtendedProject,
version: SongVersion,
existing_render: ExistingRenderResult,
render: Callable[[], Awaitable[RenderResult]],
*,
verbose: int,
Expand All @@ -343,18 +320,11 @@ async def _print_stats_for_render(
Returns the rendered file, after pretty printing itsprogress and metadata.
"""
name = version.name_for_project_dir(Path(project.path))
out_fil = version.path_for_project_dir(Path(project.path))

before_stats = summary_stats_for_file(out_fil) if out_fil.is_file() else {}
before_stats = existing_render.summary_stats
out = await render()
after_stats = (
summary_stats_for_file(out_fil, verbose=verbose)
if out_fil.is_file()
else {}
)
after_stats = out.summary_stats

self.console.print(f"[b default]{name}[/b default]")
self.console.print(f"[b default]{out.name}[/b default]")
self.console.print(f"[default dim italic]{out.fil}[/default dim italic]")

table = rich.table.Table(
Expand Down
60 changes: 58 additions & 2 deletions src/music/render/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,48 @@
from functools import cached_property
from pathlib import Path

from music.__codegen__ import stats
from music.util import ExtendedProject, SongVersion

class RenderResult:

class ExistingRenderResult:
"""Summary statistics of an audio render.
Rounds times to the nearest second. Microseconds are irrelevant for human DAW operators.
"""

def __init__(self, fil: Path, render_delta: datetime.timedelta):
def __init__(self, project: ExtendedProject, version: SongVersion):
"""Initialize."""
self.project = project
self.version = version
self.fil = version.path_for_project_dir(Path(project.path))

@property
def name(self) -> str:
"""Name of the project."""
return self.version.name_for_project_dir(Path(self.project.path))

@cached_property
def summary_stats(self) -> dict[str, float | str]:
"""Statistics for the given audio file, like LUFS-I and LRA."""
return summary_stats_for_file(self.fil) if self.fil.is_file() else {}


class RenderResult(ExistingRenderResult):
"""Summary statistics of an audio render.
Rounds times to the nearest second. Microseconds are irrelevant for human DAW operators.
"""

def __init__(
self,
project: ExtendedProject,
version: SongVersion,
fil: Path,
render_delta: datetime.timedelta,
):
"""Override. Initialize."""
super().__init__(project, version)
self.fil = fil
self.render_delta = datetime.timedelta(seconds=round(render_delta.seconds))

Expand Down Expand Up @@ -48,3 +81,26 @@ def render_speedup(self) -> float:
return (
(self.duration_delta / self.render_delta) if self.render_delta else math.inf
)


def summary_stats_for_file(fil: Path, *, verbose: int = 0) -> dict[str, float | str]:
"""Print statistics for the given audio file, like LUFS-I and LRA."""
cmd = _cmd_for_stats(fil)
proc = subprocess.run(cmd, check=True, stderr=subprocess.PIPE, text=True)
proc_output = proc.stderr
return stats.parse_summary_stats(proc_output)


def _cmd_for_stats(fil: Path) -> list[str | Path]:
return [
"ffmpeg",
"-i",
fil,
"-filter:a",
",".join(("volumedetect", "ebur128=framelog=verbose")),
"-hide_banner",
"-nostats",
"-f",
"null",
"/dev/null",
]
2 changes: 1 addition & 1 deletion src/music/stat/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def main(files_or_project_dirs: list[Path], verbose: int) -> None:
print()
if len(files) > 1:
print(fil)
for k, v in music.render.process.summary_stats_for_file(
for k, v in music.render.result.summary_stats_for_file(
fil, verbose=verbose
).items():
print(f"{k:<16}: {v:<32}")
Expand Down
6 changes: 4 additions & 2 deletions tests/render/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ def test_render_result_render_speedup(
duration=23.209501
[/FORMAT]
"""
project = mock.Mock(path="some/path")
version = mock.Mock()

some_file = tmp_path / "foo.wav"
some_file.touch()

obj1 = RenderResult(some_file, datetime.timedelta(seconds=4.5))
obj2 = RenderResult(some_file, datetime.timedelta(seconds=0.1))
obj1 = RenderResult(project, version, some_file, datetime.timedelta(seconds=4.5))
obj2 = RenderResult(project, version, some_file, datetime.timedelta(seconds=0.1))

assert obj1.render_speedup == 5.75
assert obj2.render_speedup == math.inf
Expand Down
4 changes: 2 additions & 2 deletions tests/test_stat.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from music.stat.command import main as stat


@mock.patch("music.render.process.summary_stats_for_file")
@mock.patch("music.render.result.summary_stats_for_file")
def test_main_files(
mock_stats_for_file: mock.Mock, snapshot: SnapshotAssertion, tmp_path: Path
) -> None:
Expand All @@ -35,7 +35,7 @@ def test_main_files(
assert mock_stats_for_file.mock_calls == snapshot


@mock.patch("music.render.process.summary_stats_for_file")
@mock.patch("music.render.result.summary_stats_for_file")
@mock.patch("music.util.ExtendedProject", autospec=True)
def test_main_no_args(
mock_project: mock.Mock,
Expand Down

0 comments on commit 4334349

Please sign in to comment.