harp-dev Claude Opus 5.5 commited on
Commit
01f0475
·
verified ·
1 Parent(s): e78811f

Build the default model in the worker, clean up, and declare the weights' license

Browse files

Moves the model card to module scope, builds and places the default model in the worker and fetches the others there, removes debug prints, dead code, and the nested and unused GPU functions, and declares CC-BY-NC-SA-4.0, the license the authors state for the weights.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Files changed (2) hide show
  1. README.md +9 -233
  2. app.py +58 -231
README.md CHANGED
@@ -8,7 +8,7 @@ sdk_version: 6.24.0
8
  python_version: 3.11
9
  app_file: app.py
10
  pinned: false
11
- license: mit
12
  short_description: Remixes and extends audio by masked generation
13
  tags:
14
  - category:generation
@@ -22,237 +22,13 @@ tags:
22
 
23
  # VampNet
24
 
25
- # Table of contents
26
-
27
- - [setting up](#setting-up)
28
- - [programmatic usage](#programmatic-usage)
29
- - [launching the web app](#launching-the-web-app)
30
- - [training / fine-tuning](#training--fine-tuning)
31
- - [training a model](#training-a-model)
32
- - [debugging training](#debugging-training)
33
- - [fine-tuning](#fine-tuning)
34
- - [exporting your model](#exporting-your-model)
35
- - [unloop](#unloop)
36
- - [token telephone](#token-telephone)
37
- - [a note on argbind](#a-note-on-argbind)
38
- - [take a look at the pretrained models](#take-a-look-at-the-pretrained-models)
39
- - [licensing for pretrained models](#licensing-for-pretrained-models)
40
-
41
- ## setting up
42
-
43
- python 3.9-3.11 works well. (for example, using conda)
44
- ```bash
45
- conda create -n vampnet python=3.9
46
- conda activate vampnet
47
- ```
48
-
49
- install VampNet
50
-
51
- ```bash
52
- git clone https://github.com/hugofloresgarcia/vampnet.git
53
- pip install -e ./vampnet
54
- ```
55
-
56
- ## programmatic usage
57
-
58
- quick start!
59
- ```python
60
- import random
61
- import vampnet
62
- import audiotools as at
63
-
64
- # load the default vampnet model
65
- interface = vampnet.interface.Interface.default()
66
-
67
- # list available finetuned models
68
- finetuned_model_choices = interface.available_models()
69
- print(f"available finetuned models: {finetuned_model_choices}")
70
-
71
- # pick a random finetuned model
72
- model_choice = random.choice(finetuned_model_choices)
73
- print(f"choosing model: {model_choice}")
74
-
75
- # load a finetuned model
76
- interface.load_finetuned(model_choice)
77
-
78
- # load an example audio file
79
- signal = at.AudioSignal("assets/example.wav")
80
-
81
- # get the tokens for the audio
82
- codes = interface.encode(signal)
83
-
84
- # build a mask for the audio
85
- mask = interface.build_mask(
86
- codes, signal,
87
- periodic_prompt=7,
88
- upper_codebook_mask=3,
89
- )
90
-
91
- # generate the output tokens
92
- output_tokens = interface.vamp(
93
- codes, mask, return_mask=False,
94
- temperature=1.0,
95
- typical_filtering=True,
96
- )
97
-
98
- # convert them to a signal
99
- output_signal = interface.decode(output_tokens)
100
-
101
- # save the output signal
102
- output_signal.write("scratch/output.wav")
103
- ```
104
-
105
-
106
- # Launching the Web app
107
- You can launch a gradio UI to play with vampnet.
108
-
109
- ```bash
110
- python app.py
111
- ```
112
-
113
- # Training / Fine-tuning
114
-
115
- ## Training a model
116
-
117
- To train a model, run the following script:
118
-
119
- ```bash
120
- python scripts/exp/train.py --args.load conf/vampnet.yml --save_path /path/to/checkpoints
121
- ```
122
-
123
- for multi-gpu training, use torchrun:
124
-
125
- ```bash
126
- torchrun --nproc_per_node gpu scripts/exp/train.py --args.load conf/vampnet.yml --save_path path/to/ckpt
127
- ```
128
-
129
- You can edit `conf/vampnet.yml` to change the dataset paths or any training hyperparameters.
130
-
131
- For coarse2fine models, you can use `conf/c2f.yml` as a starting configuration.
132
-
133
- See `python scripts/exp/train.py -h` for a list of options.
134
-
135
- ## Debugging training
136
-
137
- To debug training, it's easier to debug with 1 gpu and 0 workers
138
-
139
- ```bash
140
- CUDA_VISIBLE_DEVICES=0 python -m pdb scripts/exp/train.py --args.load conf/vampnet.yml --save_path /path/to/checkpoints --num_workers 0
141
- ```
142
-
143
- # Fine-tuning
144
-
145
- To fine-tune a model, use the script in `scripts/exp/fine_tune.py`
146
-
147
- for an audio folder
148
- ```bash
149
- python scripts/exp/fine_tune.py /path/to/audio/folder <fine_tune_name>
150
- ```
151
-
152
- for multiple files
153
- ```bash
154
- python scripts/exp/fine_tune.py "/path/to/audio1.mp3 /path/to/audio2/ /path/to/audio3.wav" <fine_tune_name>
155
- ```
156
-
157
- This creates configuration files for a fine tuning train job. The save_paths will be set to `runs/<fine_tune_name>/coarse` and `runs/<fine_tune_name>/c2f`.
158
-
159
- launch the coarse job:
160
- ```bash
161
- python scripts/exp/train.py --args.load conf/generated/<fine_tune_name>/coarse.yml
162
- ```
163
-
164
- this will save the coarse model to `runs/<fine_tune_name>/coarse/ckpt/best/`.
165
-
166
- launch the c2f job:
167
- ```bash
168
- python scripts/exp/train.py --args.load conf/generated/<fine_tune_name>/c2f.yml
169
- ```
170
-
171
- # Resuming a Training/Finetuning Job from checkpoint.
172
-
173
- To resume from checkpoint, use the `--resume` flag and the `--save_path` to point to the checkpoint you want to resume from.
174
- ```bash
175
- python scripts/exp/train.py --args.load conf/generated/steve/coarse.yml --save_path runs/steve/coarse --resume
176
- ```
177
-
178
- # Exporting your model
179
-
180
- Once your model has been fine-tuned, you can export it to a HuggingFace model.
181
-
182
- In order to use your model in `app.py`, you will need to export it to HuggingFace.
183
-
184
- **NOTE**: In order to export, you will need a [huggingface account](https://huggingface.co/).
185
-
186
- Now, log in to huggingface using the command line:
187
- ```bash
188
- huggingface-cli login
189
- ```
190
-
191
- replace the contents of the file named `./DEFAULT_HF_MODEL_REPO` with your `<HUGGINGFACE_USERNAME>/vampnet`. A model repo will be automatically created for you with `export.py`. The default is `hugggof/vampnet`.
192
-
193
- for example, if my username is `hugggof`, I would run the following command:`
194
- ```bash
195
- echo 'hugggof/vampnet' > ./DEFAULT_HF_MODEL_REPO
196
- ```
197
-
198
- Now, run the following command to export your model (replace `<your_finetuned_model_name>` with the name of your model):
199
-
200
- ```bash
201
- python scripts/exp/export.py --name <your_finetuned_model_name> --model latest
202
- ```
203
-
204
- Once that's done, your model should appear on the list of available models in the gradio interface.
205
- Simply run `python app.py` and select your model from the dropdown list.
206
-
207
-
208
- # Unloop
209
-
210
- Make sure you have Max installed on your laptop!
211
-
212
- **NOTE**: To run unloop (with a GPU-powered server), you will need to install the vampnet repo in both your local machine and your GPU server.
213
-
214
- ## start a vampnet gradio server
215
-
216
- First, **on your GPU server**, run the gradio server:
217
- ```bash
218
- python app.py --args.load conf/interface.yml --Interface.device cuda
219
- ```
220
- This will run a vampnet gradio API on your GPU server. Copy the address. It will be something like `https://127.0.0.1:7860/`.
221
-
222
- **IMPORTANT** Make sure that this gradio port (by default `7860`) is forwarded to your local machine, where you have Max installed.
223
-
224
- ## start the unloop gradio client
225
- Now, **on your local machine**, run the unloop gradio client.
226
- ```
227
- cd unloop
228
- pip install -r requirements.txt
229
- python client.py --vampnet_url https://127.0.0.1:7860/ # replace with your gradio server address
230
- ```
231
- This will start a gradio client that connects to the gradio server running on your GPU server.
232
-
233
- ## start the unloop Max patch
234
- Now, open the unloop Max patch. It's located at `unloop/max/unloop.maxpat`.
235
-
236
- In the tape controls, check the heartbeat (`<3`) to make sure the connection to the local gradio client is working.
237
-
238
- have fun!
239
-
240
- # Token Telephone
241
-
242
- Instructions forthcoming, but the sauce is in `token_telephone/tt.py`
243
-
244
- ## A note on argbind
245
- This repository relies on [argbind](https://github.com/pseeth/argbind) to manage CLIs and config files.
246
- Config files are stored in the `conf/` folder.
247
-
248
- ### Take a look at the pretrained models
249
- All the pretrained models (trained by hugo) are stored here: https://huggingface.co/hugggof/vampnet
250
-
251
- ### Licensing for Pretrained Models:
252
- The weights for the models are licensed [`CC BY-NC-SA 4.0`](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ml). Likewise, any VampNet models fine-tuned on the pretrained models are also licensed [`CC BY-NC-SA 4.0`](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ml).
253
-
254
- Download the pretrained models from [this link](https://zenodo.org/record/8136629). Then, extract the models to the `models/` folder.
255
-
256
-
257
 
 
 
 
 
258
 
 
 
8
  python_version: 3.11
9
  app_file: app.py
10
  pinned: false
11
+ license: cc-by-nc-sa-4.0
12
  short_description: Remixes and extends audio by masked generation
13
  tags:
14
  - category:generation
 
22
 
23
  # VampNet
24
 
25
+ Remixes and extends audio by masked acoustic token modeling, with
26
+ [VampNet](https://github.com/hugofloresgarcia/vampnet) and its fine-tuned
27
+ models.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
+ The code is MIT. The pretrained weights are
30
+ [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/), as are
31
+ models fine-tuned from them, so results cannot be used commercially and must be
32
+ shared alike.
33
 
34
+ A HARP deployment. See https://github.com/TEAMuP-dev/HARP
app.py CHANGED
@@ -1,4 +1,4 @@
1
- import spaces
2
 
3
  try: # torch>=2.6 flipped torch.load(weights_only) to True; legacy ckpts need False
4
  import torch as _torch
@@ -17,165 +17,80 @@ try: # torch>=2.6 flipped torch.load(weights_only) to True; legacy ckpts need F
17
  except Exception: # torch not installed / unexpected API -- nothing to patch
18
  pass
19
 
20
- from pathlib import Path
21
- import yaml
22
  import time
23
- import uuid
24
 
25
  import numpy as np
26
  import soundfile as sf
27
  import audiotools as at
28
- import argbind
29
- import shutil
30
  import torch
31
- from datetime import datetime
32
- from pyharp.core import build_endpoint, ModelCard
33
- from pyharp.media.audio import save_audio
34
  from pyharp.tags import Subcategory, SampleRate, Channels
35
 
36
  import gradio as gr
37
- from vampnet.interface import Interface, signal_concat
38
  from vampnet import mask as pmask
39
 
40
- device="cpu"
41
- print(f"using device {device}\n"*10)
42
-
43
- interface = Interface.default()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  init_model_choice = open("DEFAULT_MODEL").read().strip()
 
45
 
46
- # load the init model
47
- interface.load_finetuned(init_model_choice)
48
-
49
- # The other fine-tuned models the interface offers are fetched in the worker, which runs
50
- # process_fn, rather than inside the GPU call the first time a request picks one
51
  if __name__ != "__main__":
52
  from vampnet import download_finetuned
53
- for _name in interface.available_models():
 
 
 
 
54
  if _name != "default":
55
  download_finetuned(_name)
56
- print(f"Loaded initial model: {init_model_choice}")
57
- print(f"Coarse model path: {interface.coarse_path}")
58
- print(f"C2F model path: {interface.c2f_path}\n")
59
-
60
-
61
- def to_output(sig):
62
- return sig.sample_rate, sig.cpu().detach().numpy()[0][0]
63
-
64
- MAX_DURATION_S = 10
65
- def load_audio(file):
66
- print(file)
67
- if isinstance(file, str):
68
- filepath = file
69
- elif isinstance(file, tuple):
70
- # not a file
71
- sr, samples = file
72
- samples = samples / np.iinfo(samples.dtype).max
73
- return sr, samples
74
- else:
75
- filepath = file.name
76
- sig = at.AudioSignal.salient_excerpt(
77
- filepath, duration=MAX_DURATION_S
78
- )
79
- sig = at.AudioSignal(filepath)
80
- return to_output(sig)
81
-
82
 
83
 
84
  from torch_pitch_shift import pitch_shift, get_fast_shifts
85
  def shift_pitch(signal, interval: int):
86
  signal.samples = pitch_shift(
87
- signal.samples,
88
- shift=interval,
89
  sample_rate=signal.sample_rate
90
  )
91
  return signal
92
 
93
 
94
- def onsets(sig: at.AudioSignal, hop_length: int):
95
- assert sig.batch_size == 1, "batch size must be 1"
96
- assert sig.num_channels == 1, "mono signals only"
97
- import librosa
98
- onset_frame_idxs = librosa.onset.onset_detect(
99
- y=sig.samples[0][0].detach().cpu().numpy(), sr=sig.sample_rate,
100
- hop_length=hop_length,
101
- backtrack=True,
102
- )
103
- return onset_frame_idxs
104
-
105
-
106
- @spaces.GPU
107
- def new_vampnet_mask(self,
108
- codes,
109
- onset_idxs,
110
- width: int = 5,
111
- periodic_prompt=2,
112
- upper_codebook_mask=1,
113
- drop_amt: float = 0.1
114
- ):
115
- from vampnet.newmask import mask_and, mask_or, onset_mask, periodic_mask, drop_ones, codebook_mask
116
- mask = mask_and(
117
- periodic_mask(codes, periodic_prompt, 1, random_roll=False),
118
- mask_or( # this re-masks the onsets, according to a periodic schedule
119
- onset_mask(onset_idxs, codes, width=width),
120
- periodic_mask(codes, periodic_prompt, 1, random_roll=False),
121
- )
122
- ).int()
123
- # make sure the onset idxs themselves are unmasked
124
- # mask = 1 - mask
125
- mask[:, :, onset_idxs] = 0
126
- mask = mask.cpu() # debug
127
- mask = 1-drop_ones(1-mask, drop_amt)
128
- mask = codebook_mask(mask, upper_codebook_mask)
129
-
130
-
131
- mask = mask.to(self.device)
132
- return mask[:, :, :]
133
-
134
-
135
-
136
- @spaces.GPU
137
  def _vamp_internal(
138
- seed, input_audio, model_choice,
139
- pitch_shift_amt, periodic_p,
140
- n_mask_codebooks, onset_mask_width,
141
- dropout, sampletemp, typical_filtering,
142
- typical_mass, typical_min_tokens, top_p,
143
- sample_cutoff, stretch_factor, sampling_steps, beat_mask_ms, num_feedback_steps, api=False, harp=False
144
  ):
145
- if torch.cuda.is_available():
146
- device = "cuda"
147
- elif torch.backends.mps.is_available():
148
- device = "mps"
149
- else:
150
- device = "cpu"
151
-
152
-
153
- print("args!")
154
- print(f"seed: {seed}")
155
- print(f"input_audio: {input_audio}")
156
- print(f"model_choice: {model_choice}")
157
- print(f"pitch_shift_amt: {pitch_shift_amt}")
158
- print(f"periodic_p: {periodic_p}")
159
- print(f"n_mask_codebooks: {n_mask_codebooks}")
160
- print(f"onset_mask_width: {onset_mask_width}")
161
- print(f"dropout: {dropout}")
162
- print(f"sampletemp: {sampletemp}")
163
- print(f"typical_filtering: {typical_filtering}")
164
- print(f"typical_mass: {typical_mass}")
165
- print(f"typical_min_tokens: {typical_min_tokens}")
166
- print(f"top_p: {top_p}")
167
- print(f"sample_cutoff: {sample_cutoff}")
168
- print(f"stretch_factor: {stretch_factor}")
169
- print(f"sampling_steps: {sampling_steps}")
170
- print(f"api: {api}")
171
- print(f"beat_mask_ms: {beat_mask_ms}")
172
- print(f"using device {interface.device}")
173
- print(f"num feedback steps: {num_feedback_steps}")
174
-
175
-
176
  t0 = time.time()
177
- interface.to(device)
178
- print(f"using device {interface.device}")
179
  _seed = seed if seed > 0 else None
180
  if _seed is None:
181
  _seed = int(torch.randint(0, 2**32, (1,)).item())
@@ -185,7 +100,7 @@ def _vamp_internal(
185
  raise gr.Error("no input audio received!")
186
  sr, input_audio = input_audio
187
  input_audio = input_audio / np.iinfo(input_audio.dtype).max
188
-
189
  sig = at.AudioSignal(input_audio, sr).to_mono()
190
 
191
  loudness = sig.loudness()
@@ -199,31 +114,15 @@ def _vamp_internal(
199
 
200
  codes = interface.encode(sig)
201
 
202
- # mask = new_vampnet_mask(
203
- # interface,
204
- # codes,
205
- # onset_idxs=onsets(sig, hop_length=interface.codec.hop_length),
206
- # width=onset_mask_width,
207
- # periodic_prompt=periodic_p,
208
- # upper_codebook_mask=n_mask_codebooks,
209
- # drop_amt=dropout
210
- # ).long()
211
-
212
-
213
  mask = interface.build_mask(
214
  codes,
215
- sig=sig,
216
  periodic_prompt=periodic_p,
217
  onset_mask_width=onset_mask_width,
218
  _dropout=dropout,
219
  upper_codebook_mask=n_mask_codebooks,
220
  )
221
  if beat_mask_ms > 0:
222
- # bm = pmask.mask_or(
223
- # pmask.periodic_mask(
224
- # codes, periodic_p, random_roll=False
225
- # ),
226
- # )
227
  mask = pmask.mask_and(
228
  mask, interface.make_beat_mask(
229
  sig, after_beat_s=beat_mask_ms/1000.,
@@ -248,9 +147,9 @@ def _vamp_internal(
248
  time_stretch_factor=stretch_factor,
249
  return_mask=True,
250
  temperature=sampletemp,
251
- typical_filtering=typical_filtering,
252
- typical_mass=typical_mass,
253
- typical_min_tokens=typical_min_tokens,
254
  top_p=top_p,
255
  seed=_seed,
256
  sample_cutoff=sample_cutoff,
@@ -260,55 +159,7 @@ def _vamp_internal(
260
  sig = interface.decode(codes)
261
  sig = sig.normalize(loudness)
262
 
263
- import matplotlib.pyplot as plt
264
- plt.clf()
265
- # plt.imshow(mask_z[0].cpu().numpy(), aspect='auto
266
-
267
- if harp:
268
- return sig
269
-
270
- return to_output(sig[0]), to_output(sig[1])
271
-
272
- @spaces.GPU
273
- def vamp(input_audio,
274
- sampletemp,
275
- top_p,
276
- periodic_p,
277
- dropout,
278
- stretch_factor,
279
- onset_mask_width,
280
- typical_filtering,
281
- typical_mass,
282
- typical_min_tokens,
283
- seed,
284
- model_choice,
285
- n_mask_codebooks,
286
- pitch_shift_amt,
287
- sample_cutoff,
288
- sampling_steps,
289
- beat_mask_ms,
290
- num_feedback_steps):
291
- return _vamp_internal(
292
- seed=seed,
293
- input_audio=input_audio,
294
- model_choice=model_choice,
295
- pitch_shift_amt=pitch_shift_amt,
296
- periodic_p=periodic_p,
297
- n_mask_codebooks=n_mask_codebooks,
298
- onset_mask_width=onset_mask_width,
299
- dropout=dropout,
300
- sampletemp=sampletemp,
301
- typical_filtering=typical_filtering,
302
- typical_mass=typical_mass,
303
- typical_min_tokens=typical_min_tokens,
304
- top_p=top_p,
305
- sample_cutoff=sample_cutoff,
306
- stretch_factor=stretch_factor,
307
- sampling_steps=sampling_steps,
308
- beat_mask_ms=beat_mask_ms,
309
- num_feedback_steps=num_feedback_steps,
310
- api=False,
311
- )
312
 
313
 
314
  # Generation covers the whole input, stretched, in 10 s chunks, so the cost grows with the
@@ -330,6 +181,8 @@ def process_fn(input_audio, sampletemp, periodic_p, dropout, n_mask_codebooks, m
330
  stretch_factor):
331
  # Checked before the GPU is reserved, so a request the model cannot take spends none of the
332
  # caller's ZeroGPU allowance
 
 
333
  if not input_audio:
334
  raise gr.Error("Provide the audio to rework.")
335
  try:
@@ -367,7 +220,7 @@ def harp_vamp(input_audio, sampletemp, periodic_p, dropout, n_mask_codebooks, mo
367
  input_audio = (sig.sample_rate, input_audio)
368
 
369
  sig = _vamp_internal(
370
- seed=0,
371
  input_audio=input_audio,
372
  model_choice=model_choice,
373
  pitch_shift_amt=0,
@@ -385,8 +238,6 @@ def harp_vamp(input_audio, sampletemp, periodic_p, dropout, n_mask_codebooks, mo
385
  sampling_steps=36,
386
  beat_mask_ms=int(0),
387
  num_feedback_steps=1,
388
- api=False,
389
- harp=True,
390
  )
391
 
392
  sig = sig.detach().cpu()
@@ -403,26 +254,6 @@ def harp_vamp(input_audio, sampletemp, periodic_p, dropout, n_mask_codebooks, mo
403
 
404
  if __name__ == "__main__":
405
  with gr.Blocks() as demo:
406
- #NEW: HARP endpoint (new PyHARP API)
407
- harp_model_card = ModelCard(
408
- name="VampNet",
409
- description=(
410
- "Generates new audio by remixing and extending your input, filling in "
411
- "the blanks to create a coherent continuation in the same style. "
412
- "Available models include: cat10: fine-tuned on expressive cat sounds; "
413
- "lazaro-ros-sep: percussion extracted from the Lázaro Ros discography, "
414
- "rich in Afro-Cuban rhythm supermetroid: trained on Super Metroid "
415
- "(SNES) gameplay audio for retro, game-style soundscapes. Generation "
416
- "runs in mono at the model's own rate; the result is resampled back to "
417
- "the input's rate and copied across its channels, so the output has the "
418
- "format it came in with but no stereo image. Repository: "
419
- "https://github.com/hugofloresgarcia/vampnet Paper: "
420
- "https://arxiv.org/abs/2307.04686"
421
- ),
422
- author="Hugo Flores Garcia, Prem Seetharaman, Rithesh Kumar, Bryan Pardo",
423
- tags=[Subcategory.INFILLING, SampleRate(44100), Channels(1), 'sound-effects']
424
- )
425
-
426
  harp_input_components = [
427
  gr.Audio(type="filepath", label="Input Audio").harp_required(True).set_info((
428
  "The audio to rework. The model masks out parts of this recording "
@@ -436,7 +267,7 @@ if __name__ == "__main__":
436
  info="How much of the input the model changes. 0 = close copy, higher = greater remix."),
437
  gr.Slider(label="Timbral Reference", value=3, minimum=1, maximum=14, step=1,
438
  info="Controls the richness of timbre retained from the source audio. Lower = simpler, higher = richer timbre."),
439
- gr.Dropdown(label="Model Choice", choices=list(interface.available_models()), value=init_model_choice,
440
  info="Select which fine-tuned VampNet model to use."),
441
  gr.Slider(label="Time Stretch Factor", minimum=1, maximum=8, step=1, value=1,
442
  info="Scales the output's length relative to the input. 1 = the same length, 2 = twice as long."),
@@ -449,15 +280,11 @@ if __name__ == "__main__":
449
  ]
450
 
451
  build_endpoint(
452
- model_card=harp_model_card,
453
  input_components=harp_input_components,
454
  output_components=harp_output_components,
455
  process_fn=process_fn,
456
  )
457
 
458
- try:
459
- demo.queue()
460
- demo.launch(pwa=True, show_error=True)
461
- except KeyboardInterrupt:
462
- shutil.rmtree("gradio-outputs", ignore_errors=True)
463
- raise
 
1
+ import spaces # imported first, as ZeroGPU requires
2
 
3
  try: # torch>=2.6 flipped torch.load(weights_only) to True; legacy ckpts need False
4
  import torch as _torch
 
17
  except Exception: # torch not installed / unexpected API -- nothing to patch
18
  pass
19
 
 
 
20
  import time
 
21
 
22
  import numpy as np
23
  import soundfile as sf
24
  import audiotools as at
 
 
25
  import torch
26
+ from pyharp.core import build_endpoint, ModelCard
27
+ from pyharp.media.audio import save_audio
 
28
  from pyharp.tags import Subcategory, SampleRate, Channels
29
 
30
  import gradio as gr
31
+ from vampnet.interface import Interface
32
  from vampnet import mask as pmask
33
 
34
+ model_card = ModelCard(
35
+ name="VampNet",
36
+ description=(
37
+ "Generates new audio by remixing and extending your input, filling in "
38
+ "the blanks to create a coherent continuation in the same style. "
39
+ "Available models include cat10, fine-tuned on expressive cat sounds; "
40
+ "lazaro-ros-sep, percussion extracted from the Lázaro Ros discography, "
41
+ "rich in Afro-Cuban rhythm; and supermetroid, trained on Super Metroid "
42
+ "(SNES) gameplay audio for retro, game-style soundscapes. Generation "
43
+ "runs in mono at the model's own rate; the result is resampled back to "
44
+ "the input's rate and copied across its channels, so the output has the "
45
+ "format it came in with but no stereo image. The code is MIT, but the "
46
+ "pretrained weights are CC-BY-NC-SA-4.0, so results cannot be used "
47
+ "commercially and must be shared alike. Repository: "
48
+ "https://github.com/hugofloresgarcia/vampnet Paper: "
49
+ "https://arxiv.org/abs/2307.04686"
50
+ ),
51
+ author="Hugo Flores Garcia, Prem Seetharaman, Rithesh Kumar, Bryan Pardo",
52
+ tags=[Subcategory.INFILLING, SampleRate(44100), Channels(1), 'sound-effects']
53
+ )
54
+
55
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
56
  init_model_choice = open("DEFAULT_MODEL").read().strip()
57
+ MODEL_CHOICES = Interface.available_models()
58
 
59
+ # pyharp runs process_fn in a worker, which imports this file as __mp_main__ and starts with the
60
+ # server, so the default model is built and placed there alone. Each fine-tuned model is 2.4 GB,
61
+ # too much to hold all of them, so the others are only fetched here, and built when a request
62
+ # picks one.
 
63
  if __name__ != "__main__":
64
  from vampnet import download_finetuned
65
+
66
+ interface = Interface.default()
67
+ interface.load_finetuned(init_model_choice)
68
+ interface.to(DEVICE)
69
+ for _name in MODEL_CHOICES:
70
  if _name != "default":
71
  download_finetuned(_name)
72
+ print(f"Loaded initial model: {init_model_choice}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
 
75
  from torch_pitch_shift import pitch_shift, get_fast_shifts
76
  def shift_pitch(signal, interval: int):
77
  signal.samples = pitch_shift(
78
+ signal.samples,
79
+ shift=interval,
80
  sample_rate=signal.sample_rate
81
  )
82
  return signal
83
 
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  def _vamp_internal(
86
+ seed, input_audio, model_choice,
87
+ pitch_shift_amt, periodic_p,
88
+ n_mask_codebooks, onset_mask_width,
89
+ dropout, sampletemp, typical_filtering,
90
+ typical_mass, typical_min_tokens, top_p,
91
+ sample_cutoff, stretch_factor, sampling_steps, beat_mask_ms, num_feedback_steps
92
  ):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  t0 = time.time()
 
 
94
  _seed = seed if seed > 0 else None
95
  if _seed is None:
96
  _seed = int(torch.randint(0, 2**32, (1,)).item())
 
100
  raise gr.Error("no input audio received!")
101
  sr, input_audio = input_audio
102
  input_audio = input_audio / np.iinfo(input_audio.dtype).max
103
+
104
  sig = at.AudioSignal(input_audio, sr).to_mono()
105
 
106
  loudness = sig.loudness()
 
114
 
115
  codes = interface.encode(sig)
116
 
 
 
 
 
 
 
 
 
 
 
 
117
  mask = interface.build_mask(
118
  codes,
119
+ sig=sig,
120
  periodic_prompt=periodic_p,
121
  onset_mask_width=onset_mask_width,
122
  _dropout=dropout,
123
  upper_codebook_mask=n_mask_codebooks,
124
  )
125
  if beat_mask_ms > 0:
 
 
 
 
 
126
  mask = pmask.mask_and(
127
  mask, interface.make_beat_mask(
128
  sig, after_beat_s=beat_mask_ms/1000.,
 
147
  time_stretch_factor=stretch_factor,
148
  return_mask=True,
149
  temperature=sampletemp,
150
+ typical_filtering=typical_filtering,
151
+ typical_mass=typical_mass,
152
+ typical_min_tokens=typical_min_tokens,
153
  top_p=top_p,
154
  seed=_seed,
155
  sample_cutoff=sample_cutoff,
 
159
  sig = interface.decode(codes)
160
  sig = sig.normalize(loudness)
161
 
162
+ return sig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
 
165
  # Generation covers the whole input, stretched, in 10 s chunks, so the cost grows with the
 
181
  stretch_factor):
182
  # Checked before the GPU is reserved, so a request the model cannot take spends none of the
183
  # caller's ZeroGPU allowance
184
+ if model_choice not in MODEL_CHOICES:
185
+ raise gr.Error(f"Unknown model: {model_choice}")
186
  if not input_audio:
187
  raise gr.Error("Provide the audio to rework.")
188
  try:
 
220
  input_audio = (sig.sample_rate, input_audio)
221
 
222
  sig = _vamp_internal(
223
+ seed=0,
224
  input_audio=input_audio,
225
  model_choice=model_choice,
226
  pitch_shift_amt=0,
 
238
  sampling_steps=36,
239
  beat_mask_ms=int(0),
240
  num_feedback_steps=1,
 
 
241
  )
242
 
243
  sig = sig.detach().cpu()
 
254
 
255
  if __name__ == "__main__":
256
  with gr.Blocks() as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  harp_input_components = [
258
  gr.Audio(type="filepath", label="Input Audio").harp_required(True).set_info((
259
  "The audio to rework. The model masks out parts of this recording "
 
267
  info="How much of the input the model changes. 0 = close copy, higher = greater remix."),
268
  gr.Slider(label="Timbral Reference", value=3, minimum=1, maximum=14, step=1,
269
  info="Controls the richness of timbre retained from the source audio. Lower = simpler, higher = richer timbre."),
270
+ gr.Dropdown(label="Model Choice", choices=list(MODEL_CHOICES), value=init_model_choice,
271
  info="Select which fine-tuned VampNet model to use."),
272
  gr.Slider(label="Time Stretch Factor", minimum=1, maximum=8, step=1, value=1,
273
  info="Scales the output's length relative to the input. 1 = the same length, 2 = twice as long."),
 
280
  ]
281
 
282
  build_endpoint(
283
+ model_card=model_card,
284
  input_components=harp_input_components,
285
  output_components=harp_output_components,
286
  process_fn=process_fn,
287
  )
288
 
289
+ demo.queue()
290
+ demo.launch(pwa=True, show_error=True)