cwitkowitz commited on
Commit
784ade4
·
1 Parent(s): 3ac71e2

Deploying refreshed UI-Tester from pyharp v0.3.1.

Browse files
Files changed (7) hide show
  1. .gitattributes +2 -0
  2. .gitignore +1 -0
  3. README.md +4 -6
  4. app.py +342 -0
  5. requirements.txt +1 -0
  6. resources/test.mid +3 -0
  7. resources/test.wav +3 -0
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.wav filter=lfs diff=lfs merge=lfs -text
37
+ *.mid filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ _outputs
README.md CHANGED
@@ -1,15 +1,13 @@
1
  ---
2
  title: UI Tester
3
- emoji: 🦀
4
  colorFrom: indigo
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 6.26.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
11
  license: mit
12
- short_description: A template exercising every supported feature of pyharp.
13
  ---
14
 
15
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: UI Tester
3
+ emoji: 🧪
4
  colorFrom: indigo
5
+ colorTo: gray
6
  sdk: gradio
7
+ sdk_version: 6.24.0
 
8
  app_file: app.py
9
  pinned: false
10
  license: mit
 
11
  ---
12
 
13
+ A PyHARP example which exercises every input control, track type, and output label type supported by HARP. Performs no real processing.
app.py ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ UI Tester: a reference app exercising every PyHARP feature.
3
+
4
+ Unlike the other examples, this app does no real processing. It exists to
5
+ verify that HARP renders each supported input control, each track type, and
6
+ each output label type correctly. Use it as a lookup for how a given
7
+ component is declared, and as a smoke test after changing HARP's UI.
8
+
9
+ Covered here:
10
+ - Track inputs and outputs: audio and MIDI
11
+ - Generic file input and output (a file picker rather than a track)
12
+ - Every control type: slider, number box, dropdown (single and
13
+ multiple selection), checkbox, text box
14
+ - Optional inputs, via harp_required(False)
15
+ - Control and output descriptions, via set_info()
16
+ - Output labels over both audio and MIDI, with every label field
17
+ """
18
+
19
+ from pyharp import *
20
+
21
+ from typing import Optional, Tuple
22
+ import gradio as gr
23
+
24
+ import time
25
+ import os
26
+
27
+
28
+ # Reference media, used only when the corresponding input track is left empty.
29
+ # Bundled alongside this app so it also works offline and once deployed.
30
+ RESOURCE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources")
31
+ REFERENCE_AUDIO_PATH = os.path.join(RESOURCE_DIR, "test.wav")
32
+ REFERENCE_MIDI_PATH = os.path.join(RESOURCE_DIR, "test.mid")
33
+
34
+ # Metadata shown in HARP's model info panel
35
+ model_card = ModelCard(
36
+ name="UI Tester",
37
+ description=(
38
+ "Exercises every input control, track type, and output label type "
39
+ "supported by HARP. No real processing is performed: input tracks are "
40
+ "passed through unchanged, and bundled reference media is substituted for any "
41
+ "track left empty, so the app can be run without loading anything. "
42
+ "The control values are echoed into the output label descriptions."
43
+ ),
44
+ author="TEAMuP",
45
+ tags=["example", "ui", "test", "v3"],
46
+ )
47
+
48
+
49
+ def build_audio_labels(duration: float, descriptions: dict) -> list:
50
+ """
51
+ Build one audio label of each supported variety.
52
+
53
+ A label with an amplitude is drawn as an overlay on the waveform, and one
54
+ without is drawn in the overhead strip above it.
55
+
56
+ Args:
57
+ duration (float): Duration of the output audio, in seconds.
58
+ descriptions (dict): Control values to echo into the labels.
59
+
60
+ Returns:
61
+ labels (list): The constructed AudioLabel objects.
62
+ """
63
+
64
+ return [
65
+ # Overhead, at the start, with a clickable link
66
+ AudioLabel(
67
+ t=0.0,
68
+ label="start",
69
+ duration=1.0,
70
+ description=descriptions["sliders"],
71
+ color=OutputLabel.rgb_color_to_int(0, 255, 0),
72
+ link="https://github.com/TEAMuP-dev/pyharp"
73
+ ),
74
+ # Overlay, pinned to the bottom of the waveform
75
+ AudioLabel(
76
+ t=0.0,
77
+ label="amplitude 0.0",
78
+ duration=1.0,
79
+ description=descriptions["dropdowns"],
80
+ color=OutputLabel.rgb_color_to_int(28, 102, 48),
81
+ amplitude=0.0
82
+ ),
83
+ # Overlay, pinned to the middle of the waveform
84
+ AudioLabel(
85
+ t=0.5 * duration,
86
+ label="amplitude 0.5",
87
+ duration=1.0,
88
+ description=descriptions["checkboxes"],
89
+ color=OutputLabel.rgb_color_to_int(102, 28, 48),
90
+ amplitude=0.5
91
+ ),
92
+ # Overhead, at the end, with no duration (a marker rather than a span)
93
+ AudioLabel(
94
+ t=duration,
95
+ label="end",
96
+ duration=0.0,
97
+ description=descriptions["text"],
98
+ color=OutputLabel.rgb_color_to_int(0, 0, 255)
99
+ ),
100
+ ]
101
+
102
+
103
+ def build_midi_labels(descriptions: dict) -> list:
104
+ """
105
+ Build one MIDI label of each supported variety.
106
+
107
+ A label with a pitch is drawn as an overlay on the corresponding piano
108
+ roll row, and one without is drawn in the overhead strip above it.
109
+
110
+ Args:
111
+ descriptions (dict): Control values to echo into the labels.
112
+
113
+ Returns:
114
+ labels (list): The constructed MidiLabel objects.
115
+ """
116
+
117
+ return [
118
+ # Overhead, spanning the first second
119
+ MidiLabel(
120
+ t=0.0,
121
+ label="start",
122
+ duration=1.0,
123
+ description=descriptions["number"],
124
+ color=OutputLabel.rgb_color_to_int(255, 255, 0)
125
+ ),
126
+ # Overlay, on the piano roll row for the given pitch
127
+ MidiLabel(
128
+ t=0.0,
129
+ label="pitch 76",
130
+ duration=1.0,
131
+ description=descriptions["text"],
132
+ color=OutputLabel.rgb_color_to_int(255, 128, 78),
133
+ pitch=76
134
+ ),
135
+ # Overlay, as a marker with a clickable link
136
+ MidiLabel(
137
+ t=1.0,
138
+ label="pitch 86",
139
+ duration=0.0,
140
+ color=OutputLabel.rgb_color_to_int(78, 128, 255),
141
+ link="https://github.com/TEAMuP-dev/pyharp",
142
+ pitch=86
143
+ ),
144
+ ]
145
+
146
+
147
+ def process_fn(
148
+ input_audio_path: Optional[str],
149
+ input_midi_path: Optional[str],
150
+ input_file_path: Optional[str],
151
+ processing_delay: float,
152
+ gain: float,
153
+ repetitions: float,
154
+ mode: str,
155
+ effects: list,
156
+ enable_audio_labels: bool,
157
+ enable_midi_labels: bool,
158
+ text_prompt: str
159
+ ) -> Tuple[str, str, LabelList, str]:
160
+ """
161
+ Pass the input tracks through and annotate them with output labels.
162
+
163
+ Args:
164
+ input_audio_path (Optional[str]): Audio track, or None if left empty.
165
+ input_midi_path (Optional[str]): MIDI track, or None if left empty.
166
+ input_file_path (Optional[str]): Generic file, or None if left empty.
167
+ processing_delay (float): Seconds to sleep, to test HARP's cancel button.
168
+ gain (float): Unused; demonstrates a fractional slider.
169
+ repetitions (float): Unused; demonstrates a number box.
170
+ mode (str): Unused; demonstrates a dropdown.
171
+ effects (list): Unused; demonstrates a multiple-selection dropdown.
172
+ enable_audio_labels (bool): Whether to emit labels over the audio.
173
+ enable_midi_labels (bool): Whether to emit labels over the MIDI.
174
+ text_prompt (str): Written to the output file when no input file is given.
175
+
176
+ Returns:
177
+ output_audio_path (str): Path to the output audio.
178
+ output_midi_path (str): Path to the output MIDI.
179
+ output_labels (LabelList): Labels drawn over the output tracks.
180
+ output_file_path (str): Path to the generic output file.
181
+ """
182
+
183
+ if input_audio_path is None:
184
+ input_audio_path = REFERENCE_AUDIO_PATH
185
+
186
+ if input_midi_path is None:
187
+ input_midi_path = REFERENCE_MIDI_PATH
188
+
189
+ audio = load_audio(input_audio_path)
190
+ midi = load_midi(input_midi_path)
191
+
192
+ output_audio_path = str(save_audio(audio))
193
+ output_midi_path = str(save_midi(midi))
194
+
195
+ # A generic file input arrives as a path, and is echoed back unchanged.
196
+ # When none is provided, write one so the output component is populated.
197
+ if input_file_path is None:
198
+ output_file_path = get_default_path(".txt")
199
+
200
+ with open(output_file_path, "w") as f:
201
+ f.write(f"text_prompt: {text_prompt}\n")
202
+ else:
203
+ output_file_path = input_file_path
204
+
205
+ # Echo the control values so they can be read back in HARP
206
+ descriptions = {
207
+ "sliders": f"processing_delay: {processing_delay}, gain: {gain}",
208
+ "number": f"repetitions: {repetitions}",
209
+ "dropdowns": f"mode: {mode}, effects: {effects}",
210
+ "checkboxes": (
211
+ f"audio_labels: {enable_audio_labels}, "
212
+ f"midi_labels: {enable_midi_labels}"
213
+ ),
214
+ "text": f"text_prompt: {text_prompt}",
215
+ }
216
+
217
+ output_labels = LabelList()
218
+
219
+ if enable_audio_labels:
220
+ # Total duration of the audio, in seconds
221
+ duration = audio.audio_data.shape[-1] / audio.sample_rate
222
+ output_labels.labels.extend(build_audio_labels(duration, descriptions))
223
+
224
+ if enable_midi_labels:
225
+ output_labels.labels.extend(build_midi_labels(descriptions))
226
+
227
+ # Stall so that HARP's cancel button and status area can be exercised
228
+ time.sleep(float(processing_delay))
229
+
230
+ return output_audio_path, output_midi_path, output_labels, output_file_path
231
+
232
+
233
+ # Build the Gradio endpoint
234
+ with gr.Blocks() as demo:
235
+ # Audio and MIDI components become tracks in HARP. A gr.File with any
236
+ # other extension becomes a GUI file picker instead. Every input here is
237
+ # optional, so the app can be run without loading anything.
238
+ # Order must match the process_fn signature.
239
+ input_components = [
240
+ gr.Audio(
241
+ type="filepath",
242
+ label="Input Audio"
243
+ )
244
+ .harp_required(False)
245
+ .set_info("Passed through unchanged. Bundled reference audio is used if empty."),
246
+ gr.File(
247
+ type="filepath",
248
+ label="Input MIDI",
249
+ file_types=[".mid", ".midi"]
250
+ )
251
+ .harp_required(False)
252
+ .set_info("Passed through unchanged. Bundled reference MIDI is used if empty."),
253
+ gr.File(
254
+ type="filepath",
255
+ label="Input File",
256
+ file_types=[".txt", ".csv", ".json", ".nam"]
257
+ )
258
+ .harp_required(False)
259
+ .set_info("A generic file. HARP shows this as a file picker, not a track."),
260
+ gr.Slider(
261
+ minimum=0,
262
+ maximum=60,
263
+ step=1,
264
+ value=0,
265
+ label="Processing Delay (s)",
266
+ info="Stalls processing, so the cancel button can be tested."
267
+ ),
268
+ gr.Slider(
269
+ minimum=0.0,
270
+ maximum=1.0,
271
+ step=0.01,
272
+ value=0.5,
273
+ label="Gain",
274
+ info="A fractional slider (unused)."
275
+ ),
276
+ gr.Number(
277
+ minimum=1,
278
+ maximum=16,
279
+ step=1,
280
+ value=4,
281
+ label="Repetitions",
282
+ info="A number box (unused)."
283
+ ),
284
+ gr.Dropdown(
285
+ choices=["first", "second", "third"],
286
+ value="second",
287
+ label="Mode",
288
+ info="A dropdown (unused)."
289
+ ),
290
+ gr.Dropdown(
291
+ choices=["reverb", "delay", "chorus"],
292
+ value=["reverb", "chorus"],
293
+ multiselect=True,
294
+ label="Effects",
295
+ info="A dropdown allowing more than one selection (unused)."
296
+ ),
297
+ gr.Checkbox(
298
+ value=True,
299
+ label="Audio Labels",
300
+ info="Emit output labels over the audio track."
301
+ ),
302
+ gr.Checkbox(
303
+ value=True,
304
+ label="MIDI Labels",
305
+ info="Emit output labels over the MIDI track."
306
+ ),
307
+ gr.Textbox(
308
+ value="Hello World",
309
+ label="Text Prompt",
310
+ info="A text box. Written to the output file when no input file is given."
311
+ ),
312
+ ]
313
+
314
+ # A gr.JSON output receives the LabelList and is drawn over the tracks.
315
+ # Order must match the values returned by process_fn.
316
+ output_components = [
317
+ gr.Audio(
318
+ type="filepath",
319
+ label="Output Audio"
320
+ ).set_info("The input audio, unchanged."),
321
+ gr.File(
322
+ type="filepath",
323
+ label="Output MIDI",
324
+ file_types=[".mid", ".midi"]
325
+ ).set_info("The input MIDI, unchanged."),
326
+ gr.JSON(
327
+ label="Output Labels"
328
+ ).set_info("Labels drawn over the output tracks."),
329
+ gr.File(
330
+ type="filepath",
331
+ label="Output File"
332
+ ).set_info("A generic file output."),
333
+ ]
334
+
335
+ app = build_endpoint(
336
+ model_card=model_card,
337
+ input_components=input_components,
338
+ output_components=output_components,
339
+ process_fn=process_fn,
340
+ )
341
+
342
+ demo.queue().launch(share=True, show_error=True, pwa=True)
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ -e git+https://github.com/TEAMuP-dev/pyharp.git#egg=pyharp
resources/test.mid ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a2c4efd5f95b0c0d9aa1b78c21bb4867a56c89b75058bc332e2369a63df6e7c7
3
+ size 244
resources/test.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:38535cbebe3d054f8624a7856891848af8f834279a2c4b063afe3e391397da68
3
+ size 451866