File size: 5,698 Bytes
2c29da2
 
64bb8a5
 
2c29da2
 
 
9c10999
 
2c29da2
8255c7d
2c29da2
 
 
 
 
 
 
ccdb35c
 
 
2c29da2
02ced71
bbac552
2c29da2
 
 
30f2487
 
 
 
 
 
 
2c29da2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd01067
 
2c29da2
 
 
 
 
 
 
 
 
 
 
 
 
fd01067
 
 
 
 
 
 
 
2c29da2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
07115fc
 
 
 
 
 
 
 
 
c2d7fd1
 
 
 
07115fc
 
 
 
 
c2d7fd1
 
 
 
07115fc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c2d7fd1
 
 
07115fc
 
 
 
 
 
 
2c29da2
 
79fe149
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
from __future__ import annotations

import spaces  # imported first, as ZeroGPU requires

import gradio as gr
import soundfile as sf

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

from stable_audio_runtime import edit_audio, load_model

MIN_AUDIO_SECONDS = 5
MAX_AUDIO_SECONDS = 30

model_card = ModelCard(
    name="Stable Audio 3",
    description=(
        "Restyle, inpaint, or continue music audio using a text prompt. "
        "Repository: https://github.com/Stability-AI/stable-audio-3 Paper: "
        "https://arxiv.org/abs/2605.17991"
    ),
    author="Zach Evans, Julian D. Parker, Matthew Rice, et al.",
    tags=[Subcategory.EDITING],
)


# pyharp runs process_fn in a worker, which imports this file as __mp_main__ and starts with the
# server. stable-audio-tools is installed and the model downloaded, built and placed there, on the
# worker's main thread, so that no request does any of it inside its GPU reservation.
if __name__ != "__main__":
    load_model()


def _audio_duration(path: str | None) -> float:
    if not path:
        raise gr.Error("Please upload a music clip.")
    try:
        duration = float(sf.info(path).duration)
    except Exception as exc:
        raise gr.Error(f"Could not read the audio file: {exc}") from exc
    if duration < MIN_AUDIO_SECONDS:
        raise gr.Error(f"Audio must be at least {MIN_AUDIO_SECONDS} seconds.")
    if duration > MAX_AUDIO_SECONDS:
        raise gr.Error(f"Audio must be no longer than {MAX_AUDIO_SECONDS} seconds.")
    return duration


def process_fn(
    audio_path: str | None,
    prompt: str,
    mode: str,
    edit_start: float,
    edit_end: float,
    continuation_length: float,
    strength: float,
    seed: int,
) -> str:
    # Checked before the GPU is reserved, so a request the model cannot take spends none of the
    # caller's ZeroGPU allowance
    duration = _audio_duration(audio_path)
    prompt = (prompt or "").strip()
    if not prompt:
        raise gr.Error("Please enter an editing prompt.")

    if mode == "Inpaint" and (
        edit_start < 0 or edit_end > duration or edit_end <= edit_start
    ):
        raise gr.Error(
            "The inpaint region must be inside the uploaded clip, "
            "with the end after the start."
        )

    return run_edit(audio_path, prompt, mode, edit_start, edit_end, continuation_length,
                    strength, seed)


# A 6 s restyle measured about 24 s, and inputs are capped at 30 s
@spaces.GPU(duration=120)
def run_edit(audio_path, prompt, mode, edit_start, edit_end, continuation_length, strength,
             seed) -> str:
    try:
        return edit_audio(
            audio_path=audio_path,
            prompt=prompt,
            mode=mode,
            edit_start=float(edit_start),
            edit_end=float(edit_end),
            continuation_length=float(continuation_length),
            strength=float(strength),
            seed=int(seed),
        )
    except gr.Error:
        raise
    except Exception as exc:
        raise gr.Error(f"Stable Audio 3 inference failed: {exc}") from exc


if __name__ == "__main__":
    with gr.Blocks(title="Stable Audio 3") as demo:
        input_components = [
            gr.Audio(type="filepath", label="Music Audio")
            .harp_required(True)
            .set_info("Music clip between 5 and 30 seconds."),
            gr.Textbox(
                label="Prompt",
                placeholder="A warm synthwave groove with punchy drums",
                info=(
                    "Describe what the audio should become. What this changes depends "
                    "on the Edit Mode below."
                ),
            ).harp_required(True),
            gr.Dropdown(
                choices=["Restyle", "Inpaint", "Continue"],
                value="Restyle",
                label="Edit Mode",
                info=(
                    "Restyle reworks the whole clip, Inpaint replaces the region "
                    "between the edit points, and Continue extends past the end."
                ),
            ),
            gr.Slider(
                minimum=0,
                maximum=30,
                value=4,
                step=0.1,
                label="Edit Start (seconds)",
            ).set_info("Used by Inpaint mode."),
            gr.Slider(
                minimum=0,
                maximum=30,
                value=8,
                step=0.1,
                label="Edit End (seconds)",
            ).set_info("Used by Inpaint mode."),
            gr.Slider(
                minimum=1,
                maximum=15,
                value=8,
                step=1,
                label="Continuation Length (seconds)",
            ).set_info("Used by Continue mode."),
            gr.Slider(
                minimum=0.1,
                maximum=1.0,
                value=0.75,
                step=0.05,
                label="Transformation Strength",
            ).set_info("Used by Restyle mode."),
            gr.Slider(
                minimum=0,
                maximum=99999,
                value=0,
                step=1,
                label="Seed",
            ).set_info("Use 0 for a random seed."),
        ]
        output_components = [
            gr.Audio(type="filepath", label="Edited Audio").set_info((
                "The edited result, at 16-bit."
            )),
        ]
        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(show_error=True, pwa=True, ssr_mode=False)