roshithindia commited on
Commit
0e0d965
·
1 Parent(s): eefd49c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +216 -0
app.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from queue import Queue
2
+ from threading import Thread
3
+ from typing import Optional
4
+
5
+ import numpy as np
6
+ import torch
7
+
8
+ from transformers import MusicgenForConditionalGeneration, MusicgenProcessor, set_seed
9
+ from transformers.generation.streamers import BaseStreamer
10
+
11
+ import gradio as gr
12
+ import spaces
13
+
14
+
15
+ model = MusicgenForConditionalGeneration.from_pretrained("facebook/musicgen-small")
16
+ processor = MusicgenProcessor.from_pretrained("facebook/musicgen-small")
17
+
18
+ title = "MusicGen Streaming"
19
+
20
+ description = """
21
+ Stream the outputs of the MusicGen text-to-music model by playing the generated audio as soon as the first chunk is ready.
22
+ Demo uses [MusicGen Small](https://huggingface.co/facebook/musicgen-small) in the 🤗 Transformers library. Note that the
23
+ demo works best on the Chrome browser. If there is no audio output, try switching browser to Chrome.
24
+ """
25
+
26
+ article = """
27
+ ## Try different theme
28
+ """
29
+
30
+
31
+ class MusicgenStreamer(BaseStreamer):
32
+ def __init__(
33
+ self,
34
+ model: MusicgenForConditionalGeneration,
35
+ device: Optional[str] = None,
36
+ play_steps: Optional[int] = 10,
37
+ stride: Optional[int] = None,
38
+ timeout: Optional[float] = None,
39
+ ):
40
+ """
41
+ Streamer that stores playback-ready audio in a queue, to be used by a downstream application as an iterator. This is
42
+ useful for applications that benefit from accessing the generated audio in a non-blocking way (e.g. in an interactive
43
+ Gradio demo).
44
+ Parameters:
45
+ model (`MusicgenForConditionalGeneration`):
46
+ The MusicGen model used to generate the audio waveform.
47
+ device (`str`, *optional*):
48
+ The torch device on which to run the computation. If `None`, will default to the device of the model.
49
+ play_steps (`int`, *optional*, defaults to 10):
50
+ The number of generation steps with which to return the generated audio array. Using fewer steps will
51
+ mean the first chunk is ready faster, but will require more codec decoding steps overall. This value
52
+ should be tuned to your device and latency requirements.
53
+ stride (`int`, *optional*):
54
+ The window (stride) between adjacent audio samples. Using a stride between adjacent audio samples reduces
55
+ the hard boundary between them, giving smoother playback. If `None`, will default to a value equivalent to
56
+ play_steps // 6 in the audio space.
57
+ timeout (`int`, *optional*):
58
+ The timeout for the audio queue. If `None`, the queue will block indefinitely. Useful to handle exceptions
59
+ in `.generate()`, when it is called in a separate thread.
60
+ """
61
+ self.decoder = model.decoder
62
+ self.audio_encoder = model.audio_encoder
63
+ self.generation_config = model.generation_config
64
+ self.device = device if device is not None else model.device
65
+
66
+ # variables used in the streaming process
67
+ self.play_steps = play_steps
68
+ if stride is not None:
69
+ self.stride = stride
70
+ else:
71
+ hop_length = np.prod(self.audio_encoder.config.upsampling_ratios)
72
+ self.stride = hop_length * (play_steps - self.decoder.num_codebooks) // 6
73
+ self.token_cache = None
74
+ self.to_yield = 0
75
+
76
+ # varibles used in the thread process
77
+ self.audio_queue = Queue()
78
+ self.stop_signal = None
79
+ self.timeout = timeout
80
+
81
+ def apply_delay_pattern_mask(self, input_ids):
82
+ # build the delay pattern mask for offsetting each codebook prediction by 1 (this behaviour is specific to MusicGen)
83
+ _, decoder_delay_pattern_mask = self.decoder.build_delay_pattern_mask(
84
+ input_ids[:, :1],
85
+ pad_token_id=self.generation_config.decoder_start_token_id,
86
+ max_length=input_ids.shape[-1],
87
+ )
88
+ # apply the pattern mask to the input ids
89
+ input_ids = self.decoder.apply_delay_pattern_mask(input_ids, decoder_delay_pattern_mask)
90
+
91
+ # revert the pattern delay mask by filtering the pad token id
92
+ input_ids = input_ids[input_ids != self.generation_config.pad_token_id].reshape(
93
+ 1, self.decoder.num_codebooks, -1
94
+ )
95
+
96
+ # append the frame dimension back to the audio codes
97
+ input_ids = input_ids[None, ...]
98
+
99
+ # send the input_ids to the correct device
100
+ input_ids = input_ids.to(self.audio_encoder.device)
101
+
102
+ output_values = self.audio_encoder.decode(
103
+ input_ids,
104
+ audio_scales=[None],
105
+ )
106
+ audio_values = output_values.audio_values[0, 0]
107
+ return audio_values.cpu().float().numpy()
108
+
109
+ def put(self, value):
110
+ batch_size = value.shape[0] // self.decoder.num_codebooks
111
+ if batch_size > 1:
112
+ raise ValueError("MusicgenStreamer only supports batch size 1")
113
+
114
+ if self.token_cache is None:
115
+ self.token_cache = value
116
+ else:
117
+ self.token_cache = torch.concatenate([self.token_cache, value[:, None]], dim=-1)
118
+
119
+ if self.token_cache.shape[-1] % self.play_steps == 0:
120
+ audio_values = self.apply_delay_pattern_mask(self.token_cache)
121
+ self.on_finalized_audio(audio_values[self.to_yield : -self.stride])
122
+ self.to_yield += len(audio_values) - self.to_yield - self.stride
123
+
124
+ def end(self):
125
+ """Flushes any remaining cache and appends the stop symbol."""
126
+ if self.token_cache is not None:
127
+ audio_values = self.apply_delay_pattern_mask(self.token_cache)
128
+ else:
129
+ audio_values = np.zeros(self.to_yield)
130
+
131
+ self.on_finalized_audio(audio_values[self.to_yield :], stream_end=True)
132
+
133
+ def on_finalized_audio(self, audio: np.ndarray, stream_end: bool = False):
134
+ """Put the new audio in the queue. If the stream is ending, also put a stop signal in the queue."""
135
+ self.audio_queue.put(audio, timeout=self.timeout)
136
+ if stream_end:
137
+ self.audio_queue.put(self.stop_signal, timeout=self.timeout)
138
+
139
+ def __iter__(self):
140
+ return self
141
+
142
+ def __next__(self):
143
+ value = self.audio_queue.get(timeout=self.timeout)
144
+ if not isinstance(value, np.ndarray) and value == self.stop_signal:
145
+ raise StopIteration()
146
+ else:
147
+ return value
148
+
149
+
150
+ sampling_rate = model.audio_encoder.config.sampling_rate
151
+ frame_rate = model.audio_encoder.config.frame_rate
152
+
153
+ target_dtype = np.int16
154
+ max_range = np.iinfo(target_dtype).max
155
+
156
+
157
+ @spaces.GPU()
158
+ def generate_audio(text_prompt, audio_length_in_s=10.0, play_steps_in_s=2.0, seed=0):
159
+ max_new_tokens = int(frame_rate * audio_length_in_s)
160
+ play_steps = int(frame_rate * play_steps_in_s)
161
+
162
+ device = "cuda:0" if torch.cuda.is_available() else "cpu"
163
+ if device != model.device:
164
+ model.to(device)
165
+ if device == "cuda:0":
166
+ model.half()
167
+
168
+ inputs = processor(
169
+ text=text_prompt,
170
+ padding=True,
171
+ return_tensors="pt",
172
+ )
173
+
174
+ streamer = MusicgenStreamer(model, device=device, play_steps=play_steps)
175
+
176
+ generation_kwargs = dict(
177
+ **inputs.to(device),
178
+ streamer=streamer,
179
+ max_new_tokens=max_new_tokens,
180
+ )
181
+ thread = Thread(target=model.generate, kwargs=generation_kwargs)
182
+ thread.start()
183
+
184
+ set_seed(seed)
185
+ for new_audio in streamer:
186
+ print(f"Sample of length: {round(new_audio.shape[0] / sampling_rate, 2)} seconds")
187
+ new_audio = (new_audio * max_range).astype(np.int16)
188
+ yield sampling_rate, new_audio
189
+
190
+
191
+ demo = gr.Interface(
192
+ fn=generate_audio,
193
+ inputs=[
194
+ gr.Text(label="Prompt", value="80s pop track with synth and instrumentals"),
195
+ gr.Slider(10, 30, value=15, step=5, label="Audio length in seconds"),
196
+ gr.Slider(0.5, 2.5, value=1.5, step=0.5, label="Streaming interval in seconds", info="Lower = shorter chunks, lower latency, more codec steps"),
197
+ gr.Slider(0, 10, value=5, step=1, label="Seed for random generations"),
198
+ ],
199
+ outputs=[
200
+ gr.Audio(label="Generated Music", streaming=True, autoplay=True)
201
+ ],
202
+ examples=[
203
+ ["An 80s driving pop song with heavy drums and synth pads in the background", 30, 1.5, 5],
204
+ ["A cheerful country song with acoustic guitars", 30, 1.5, 5],
205
+ ["90s rock song with electric guitar and heavy drums", 30, 1.5, 5],
206
+ ["a light and cheerly EDM track, with syncopated drums, aery pads, and strong emotions bpm: 130", 30, 1.5, 5],
207
+ ["lofi slow bpm electro chill with organic samples", 30, 1.5, 5],
208
+ ],
209
+ title=title,
210
+ description=description,
211
+ article=article,
212
+ cache_examples=False,
213
+ )
214
+
215
+
216
+ demo.queue().launch()