File size: 3,998 Bytes
85ea235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5fc96bf
 
85ea235
 
 
 
 
 
 
 
 
 
36dbf9f
 
 
 
 
 
85ea235
36dbf9f
0fd0c69
85ea235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0fd0c69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85ea235
 
0fd0c69
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
from __future__ import annotations

import json
import uuid
from pathlib import Path
from tempfile import gettempdir

import gradio as gr
import soundfile as sf

try:
    import spaces
except ImportError:
    class spaces:
        class GPU:
            def __init__(self, func=None, duration=60):
                self.func = func

            def __call__(self, *args, **kwargs):
                if self.func is not None:
                    return self.func(*args, **kwargs)
                return args[0]

from pyharp import ModelCard, build_endpoint
from pyharp.tags import Subcategory

from deepafx_st_runtime import style_transfer

MIN_AUDIO_SECONDS = 1
MAX_AUDIO_SECONDS = 30
OUTPUT_ROOT = Path(gettempdir()) / "deepafx_st_outputs"

model_card = ModelCard(
    name="DeepAFx-ST",
    description=(
        (
            "Transfer the production style (EQ, compression) of a reference recording "
            "onto your audio. Repository: "
            "https://github.com/adobe-research/DeepAFx-ST Paper: "
            "https://arxiv.org/abs/2207.08759"
        )
    ),
    author="Christian J. Steinmetz, Nicholas J. Bryan, Joshua D. Reiss",
    tags=[Subcategory.MIXING_STYLE_TRANSFER, 'audio-effects', 'style-transfer', 'mixing', 'mastering', 'production'],
)


def _validate_audio(path: str | None, label: str) -> str:
    if not path:
        raise gr.Error(f"Please upload {label}.")
    try:
        duration = sf.info(path).duration
    except Exception as exc:
        raise gr.Error(f"Could not read {label}: {exc}") from exc
    if duration < MIN_AUDIO_SECONDS:
        raise gr.Error(
            f"{label} must be at least {MIN_AUDIO_SECONDS} second long."
        )
    if duration > MAX_AUDIO_SECONDS:
        raise gr.Error(
            f"{label} must be no longer than {MAX_AUDIO_SECONDS} seconds. "
            f"Received {duration:.1f} seconds."
        )
    return path


@spaces.GPU(duration=120)
def process_fn(
    input_path: str | None,
    reference_path: str | None,
) -> tuple[str, str]:
    input_path = _validate_audio(input_path, "input audio")
    reference_path = _validate_audio(reference_path, "reference audio")

    try:
        audio, sample_rate, params = style_transfer(input_path, reference_path)
    except Exception as exc:
        raise gr.Error(f"DeepAFx-ST inference failed: {exc}") from exc

    output_dir = OUTPUT_ROOT / uuid.uuid4().hex
    output_dir.mkdir(parents=True, exist_ok=True)

    audio_path = output_dir / "styled_output.wav"
    sf.write(str(audio_path), audio.numpy(), sample_rate)

    params_path = output_dir / "dsp_parameters.json"
    params_path.write_text(
        json.dumps(
            {"model": "DeepAFx-ST", "variant": "autodiff", **params},
            indent=2,
        )
        + "\n",
        encoding="utf-8",
    )

    return str(audio_path), str(params_path)


if __name__ == "__main__":
    with gr.Blocks(title="DeepAFx-ST") as demo:
        input_components = [
            gr.Audio(type="filepath", label="Input Audio")
            .harp_required(True)
            .set_info("Audio to process, 1 to 30 seconds."),
            gr.Audio(type="filepath", label="Reference Audio")
            .harp_required(True)
            .set_info("Target style — your input will be EQ'd and compressed to match this recording."),
        ]
        output_components = [
            gr.Audio(type="filepath", label="Styled Output")
            .set_info("Processed audio with the reference's production style."),
            gr.File(
                type="filepath",
                file_types=[".json"],
                label="DSP Parameters",
            ).set_info("Predicted EQ and compressor parameters."),
        ]
        build_endpoint(
            model_card=model_card,
            input_components=input_components,
            output_components=output_components,
            process_fn=process_fn,
        )


    demo.queue(default_concurrency_limit=1).launch(share=True, show_error=True, pwa=True)