Spaces:
Running
Running
File size: 11,644 Bytes
784ade4 | 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 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | """
UI Tester: a reference app exercising every PyHARP feature.
Unlike the other examples, this app does no real processing. It exists to
verify that HARP renders each supported input control, each track type, and
each output label type correctly. Use it as a lookup for how a given
component is declared, and as a smoke test after changing HARP's UI.
Covered here:
- Track inputs and outputs: audio and MIDI
- Generic file input and output (a file picker rather than a track)
- Every control type: slider, number box, dropdown (single and
multiple selection), checkbox, text box
- Optional inputs, via harp_required(False)
- Control and output descriptions, via set_info()
- Output labels over both audio and MIDI, with every label field
"""
from pyharp import *
from typing import Optional, Tuple
import gradio as gr
import time
import os
# Reference media, used only when the corresponding input track is left empty.
# Bundled alongside this app so it also works offline and once deployed.
RESOURCE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources")
REFERENCE_AUDIO_PATH = os.path.join(RESOURCE_DIR, "test.wav")
REFERENCE_MIDI_PATH = os.path.join(RESOURCE_DIR, "test.mid")
# Metadata shown in HARP's model info panel
model_card = ModelCard(
name="UI Tester",
description=(
"Exercises every input control, track type, and output label type "
"supported by HARP. No real processing is performed: input tracks are "
"passed through unchanged, and bundled reference media is substituted for any "
"track left empty, so the app can be run without loading anything. "
"The control values are echoed into the output label descriptions."
),
author="TEAMuP",
tags=["example", "ui", "test", "v3"],
)
def build_audio_labels(duration: float, descriptions: dict) -> list:
"""
Build one audio label of each supported variety.
A label with an amplitude is drawn as an overlay on the waveform, and one
without is drawn in the overhead strip above it.
Args:
duration (float): Duration of the output audio, in seconds.
descriptions (dict): Control values to echo into the labels.
Returns:
labels (list): The constructed AudioLabel objects.
"""
return [
# Overhead, at the start, with a clickable link
AudioLabel(
t=0.0,
label="start",
duration=1.0,
description=descriptions["sliders"],
color=OutputLabel.rgb_color_to_int(0, 255, 0),
link="https://github.com/TEAMuP-dev/pyharp"
),
# Overlay, pinned to the bottom of the waveform
AudioLabel(
t=0.0,
label="amplitude 0.0",
duration=1.0,
description=descriptions["dropdowns"],
color=OutputLabel.rgb_color_to_int(28, 102, 48),
amplitude=0.0
),
# Overlay, pinned to the middle of the waveform
AudioLabel(
t=0.5 * duration,
label="amplitude 0.5",
duration=1.0,
description=descriptions["checkboxes"],
color=OutputLabel.rgb_color_to_int(102, 28, 48),
amplitude=0.5
),
# Overhead, at the end, with no duration (a marker rather than a span)
AudioLabel(
t=duration,
label="end",
duration=0.0,
description=descriptions["text"],
color=OutputLabel.rgb_color_to_int(0, 0, 255)
),
]
def build_midi_labels(descriptions: dict) -> list:
"""
Build one MIDI label of each supported variety.
A label with a pitch is drawn as an overlay on the corresponding piano
roll row, and one without is drawn in the overhead strip above it.
Args:
descriptions (dict): Control values to echo into the labels.
Returns:
labels (list): The constructed MidiLabel objects.
"""
return [
# Overhead, spanning the first second
MidiLabel(
t=0.0,
label="start",
duration=1.0,
description=descriptions["number"],
color=OutputLabel.rgb_color_to_int(255, 255, 0)
),
# Overlay, on the piano roll row for the given pitch
MidiLabel(
t=0.0,
label="pitch 76",
duration=1.0,
description=descriptions["text"],
color=OutputLabel.rgb_color_to_int(255, 128, 78),
pitch=76
),
# Overlay, as a marker with a clickable link
MidiLabel(
t=1.0,
label="pitch 86",
duration=0.0,
color=OutputLabel.rgb_color_to_int(78, 128, 255),
link="https://github.com/TEAMuP-dev/pyharp",
pitch=86
),
]
def process_fn(
input_audio_path: Optional[str],
input_midi_path: Optional[str],
input_file_path: Optional[str],
processing_delay: float,
gain: float,
repetitions: float,
mode: str,
effects: list,
enable_audio_labels: bool,
enable_midi_labels: bool,
text_prompt: str
) -> Tuple[str, str, LabelList, str]:
"""
Pass the input tracks through and annotate them with output labels.
Args:
input_audio_path (Optional[str]): Audio track, or None if left empty.
input_midi_path (Optional[str]): MIDI track, or None if left empty.
input_file_path (Optional[str]): Generic file, or None if left empty.
processing_delay (float): Seconds to sleep, to test HARP's cancel button.
gain (float): Unused; demonstrates a fractional slider.
repetitions (float): Unused; demonstrates a number box.
mode (str): Unused; demonstrates a dropdown.
effects (list): Unused; demonstrates a multiple-selection dropdown.
enable_audio_labels (bool): Whether to emit labels over the audio.
enable_midi_labels (bool): Whether to emit labels over the MIDI.
text_prompt (str): Written to the output file when no input file is given.
Returns:
output_audio_path (str): Path to the output audio.
output_midi_path (str): Path to the output MIDI.
output_labels (LabelList): Labels drawn over the output tracks.
output_file_path (str): Path to the generic output file.
"""
if input_audio_path is None:
input_audio_path = REFERENCE_AUDIO_PATH
if input_midi_path is None:
input_midi_path = REFERENCE_MIDI_PATH
audio = load_audio(input_audio_path)
midi = load_midi(input_midi_path)
output_audio_path = str(save_audio(audio))
output_midi_path = str(save_midi(midi))
# A generic file input arrives as a path, and is echoed back unchanged.
# When none is provided, write one so the output component is populated.
if input_file_path is None:
output_file_path = get_default_path(".txt")
with open(output_file_path, "w") as f:
f.write(f"text_prompt: {text_prompt}\n")
else:
output_file_path = input_file_path
# Echo the control values so they can be read back in HARP
descriptions = {
"sliders": f"processing_delay: {processing_delay}, gain: {gain}",
"number": f"repetitions: {repetitions}",
"dropdowns": f"mode: {mode}, effects: {effects}",
"checkboxes": (
f"audio_labels: {enable_audio_labels}, "
f"midi_labels: {enable_midi_labels}"
),
"text": f"text_prompt: {text_prompt}",
}
output_labels = LabelList()
if enable_audio_labels:
# Total duration of the audio, in seconds
duration = audio.audio_data.shape[-1] / audio.sample_rate
output_labels.labels.extend(build_audio_labels(duration, descriptions))
if enable_midi_labels:
output_labels.labels.extend(build_midi_labels(descriptions))
# Stall so that HARP's cancel button and status area can be exercised
time.sleep(float(processing_delay))
return output_audio_path, output_midi_path, output_labels, output_file_path
# Build the Gradio endpoint
with gr.Blocks() as demo:
# Audio and MIDI components become tracks in HARP. A gr.File with any
# other extension becomes a GUI file picker instead. Every input here is
# optional, so the app can be run without loading anything.
# Order must match the process_fn signature.
input_components = [
gr.Audio(
type="filepath",
label="Input Audio"
)
.harp_required(False)
.set_info("Passed through unchanged. Bundled reference audio is used if empty."),
gr.File(
type="filepath",
label="Input MIDI",
file_types=[".mid", ".midi"]
)
.harp_required(False)
.set_info("Passed through unchanged. Bundled reference MIDI is used if empty."),
gr.File(
type="filepath",
label="Input File",
file_types=[".txt", ".csv", ".json", ".nam"]
)
.harp_required(False)
.set_info("A generic file. HARP shows this as a file picker, not a track."),
gr.Slider(
minimum=0,
maximum=60,
step=1,
value=0,
label="Processing Delay (s)",
info="Stalls processing, so the cancel button can be tested."
),
gr.Slider(
minimum=0.0,
maximum=1.0,
step=0.01,
value=0.5,
label="Gain",
info="A fractional slider (unused)."
),
gr.Number(
minimum=1,
maximum=16,
step=1,
value=4,
label="Repetitions",
info="A number box (unused)."
),
gr.Dropdown(
choices=["first", "second", "third"],
value="second",
label="Mode",
info="A dropdown (unused)."
),
gr.Dropdown(
choices=["reverb", "delay", "chorus"],
value=["reverb", "chorus"],
multiselect=True,
label="Effects",
info="A dropdown allowing more than one selection (unused)."
),
gr.Checkbox(
value=True,
label="Audio Labels",
info="Emit output labels over the audio track."
),
gr.Checkbox(
value=True,
label="MIDI Labels",
info="Emit output labels over the MIDI track."
),
gr.Textbox(
value="Hello World",
label="Text Prompt",
info="A text box. Written to the output file when no input file is given."
),
]
# A gr.JSON output receives the LabelList and is drawn over the tracks.
# Order must match the values returned by process_fn.
output_components = [
gr.Audio(
type="filepath",
label="Output Audio"
).set_info("The input audio, unchanged."),
gr.File(
type="filepath",
label="Output MIDI",
file_types=[".mid", ".midi"]
).set_info("The input MIDI, unchanged."),
gr.JSON(
label="Output Labels"
).set_info("Labels drawn over the output tracks."),
gr.File(
type="filepath",
label="Output File"
).set_info("A generic file output."),
]
app = build_endpoint(
model_card=model_card,
input_components=input_components,
output_components=output_components,
process_fn=process_fn,
)
demo.queue().launch(share=True, show_error=True, pwa=True)
|