mysome commited on
Commit
e60eef2
Β·
verified Β·
1 Parent(s): f9fb0bc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +211 -24
app.py CHANGED
@@ -1,28 +1,215 @@
1
  import gradio as gr
 
 
 
 
2
  import torch
3
- from transformers import AutoModelForCausalLM, AutoTokenizer
4
-
5
- MODEL_NAME = "m-a-p/YuE-s1-7B-anneal-en-cot"
6
-
7
- def generate_text(prompt):
8
- model = AutoModelForCausalLM.from_pretrained(
9
- MODEL_NAME,
10
- torch_dtype=torch.float32,
11
- attn_implementation="eager"
12
- )
13
- tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
14
-
15
- inputs = tokenizer(prompt, return_tensors="pt")
16
- outputs = model.generate(**inputs, max_new_tokens=200)
17
- return tokenizer.decode(outputs[0], skip_special_tokens=True)
18
-
19
- demo = gr.Interface(
20
- fn=generate_text,
21
- inputs=gr.Textbox(lines=3, label="Input Prompt"),
22
- outputs="text",
23
- title="YuE Text Generator",
24
- description="A Hugging Face Space for generating text using YuE 7B model."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- if __name__ == "__main__":
28
- demo.launch()
 
1
  import gradio as gr
2
+ import subprocess
3
+ import os
4
+ import tempfile
5
+ import spaces
6
  import torch
7
+ import sys
8
+ import uuid
9
+ import re
10
+ import numpy as np
11
+ import json
12
+ from omegaconf import OmegaConf
13
+ import torchaudio
14
+ from torchaudio.transforms import Resample
15
+ import soundfile as sf
16
+ from tqdm import tqdm
17
+ from einops import rearrange
18
+ from transformers import AutoTokenizer, AutoModelForCausalLM, LogitsProcessor, LogitsProcessorList
19
+ from collections import Counter
20
+
21
+ # Disable FlashAttention installation completely
22
+ print("Skipping FlashAttention installation for CPU mode...")
23
+
24
+ # Download model dependencies
25
+ from huggingface_hub import snapshot_download
26
+
27
+ folder_path = './xcodec_mini_infer'
28
+ if not os.path.exists(folder_path):
29
+ os.mkdir(folder_path)
30
+ print(f"Folder created at: {folder_path}")
31
+ else:
32
+ print(f"Folder already exists at: {folder_path}")
33
+
34
+ snapshot_download(
35
+ repo_id="m-a-p/xcodec_mini_infer",
36
+ local_dir="./xcodec_mini_infer",
37
+ )
38
+
39
+ sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'xcodec_mini_infer'))
40
+ sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'xcodec_mini_infer', 'descriptaudiocodec'))
41
+
42
+ from codecmanipulator import CodecManipulator
43
+ from mmtokenizer import _MMSentencePieceTokenizer
44
+ from models.soundstream_hubert_new import SoundStream
45
+
46
+ device = "cpu"
47
+ print("Loading model on CPU...")
48
+
49
+ model = AutoModelForCausalLM.from_pretrained(
50
+ "m-a-p/YuE-s1-7B-anneal-en-cot",
51
+ torch_dtype=torch.float32,
52
+ attn_implementation="eager", # disable flash attention
53
  )
54
+ model.to(device)
55
+ model.eval()
56
+ print("YuE model loaded successfully on CPU.")
57
+
58
+ # Codec setup
59
+ basic_model_config = './xcodec_mini_infer/final_ckpt/config.yaml'
60
+ resume_path = './xcodec_mini_infer/final_ckpt/ckpt_00360000.pth'
61
+ mmtokenizer = _MMSentencePieceTokenizer("./mm_tokenizer_v0.2_hf/tokenizer.model")
62
+ codectool = CodecManipulator("xcodec", 0, 1)
63
+ model_config = OmegaConf.load(basic_model_config)
64
+ codec_model = eval(model_config.generator.name)(**model_config.generator.config).to(device)
65
+ parameter_dict = torch.load(resume_path, map_location='cpu')
66
+ codec_model.load_state_dict(parameter_dict['codec_model'])
67
+ codec_model.eval()
68
+ print("Codec model loaded successfully.")
69
+
70
+ # ---------------- Utility Classes ---------------- #
71
+
72
+ class BlockTokenRangeProcessor(LogitsProcessor):
73
+ def __init__(self, start_id, end_id):
74
+ self.blocked_token_ids = list(range(start_id, end_id))
75
+
76
+ def __call__(self, input_ids, scores):
77
+ scores[:, self.blocked_token_ids] = -float("inf")
78
+ return scores
79
+
80
+ def load_audio_mono(filepath, sampling_rate=16000):
81
+ audio, sr = torchaudio.load(filepath)
82
+ audio = torch.mean(audio, dim=0, keepdim=True)
83
+ if sr != sampling_rate:
84
+ resampler = Resample(orig_freq=sr, new_freq=sampling_rate)
85
+ audio = resampler(audio)
86
+ return audio
87
+
88
+ def split_lyrics(lyrics: str):
89
+ pattern = r"\[(\w+)\]\s*(.*?)(?=\s*\n\[|\Z)"
90
+ segments = re.findall(pattern, lyrics, re.DOTALL)
91
+ structured_lyrics = [f"[{seg[0]}]\n{seg[1].strip()}\n\n" for seg in segments]
92
+ return structured_lyrics
93
+
94
+ # ---------------- Generation Logic ---------------- #
95
+
96
+ @spaces.GPU(duration=178)
97
+ def generate_music(
98
+ genre_txt=None,
99
+ lyrics_txt=None,
100
+ run_n_segments=2,
101
+ max_new_tokens=45,
102
+ use_audio_prompt=False,
103
+ audio_prompt_path="",
104
+ progress=gr.Progress()
105
+ ):
106
+ if use_audio_prompt and not audio_prompt_path:
107
+ raise FileNotFoundError("Please provide an audio prompt file when 'Use Audio Prompt' is enabled!")
108
+
109
+ with tempfile.TemporaryDirectory() as output_dir:
110
+ stage1_output_dir = os.path.join(output_dir, f"stage1")
111
+ os.makedirs(stage1_output_dir, exist_ok=True)
112
+
113
+ genres = genre_txt.strip()
114
+ lyrics = split_lyrics(lyrics_txt + "\n")
115
+ full_lyrics = "\n".join(lyrics)
116
+ prompt_texts = [f"Generate music from lyrics.\n[Genre] {genres}\n{full_lyrics}"]
117
+ prompt_texts += lyrics
118
+ random_id = uuid.uuid4()
119
+
120
+ top_p = 0.93
121
+ temperature = 1.0
122
+ repetition_penalty = 1.2
123
+ start_of_segment = mmtokenizer.tokenize('[start_of_segment]')
124
+ end_of_segment = mmtokenizer.tokenize('[end_of_segment]')
125
+
126
+ run_n_segments = min(run_n_segments, len(lyrics)) + 1
127
+ raw_output = None
128
+
129
+ def generator():
130
+ nonlocal raw_output
131
+ for i, p in enumerate(tqdm(prompt_texts[:run_n_segments])):
132
+ if i == 0: continue
133
+ section_text = p.replace('[start_of_segment]', '').replace('[end_of_segment]', '')
134
+ if i == 1:
135
+ head_id = mmtokenizer.tokenize(prompt_texts[0])
136
+ prompt_ids = head_id + start_of_segment + mmtokenizer.tokenize(section_text) + [mmtokenizer.soa] + codectool.sep_ids
137
+ else:
138
+ prompt_ids = end_of_segment + start_of_segment + mmtokenizer.tokenize(section_text) + [mmtokenizer.soa] + codectool.sep_ids
139
+
140
+ prompt_ids = torch.as_tensor(prompt_ids).unsqueeze(0).to(device)
141
+ input_ids = torch.cat([raw_output, prompt_ids], dim=1) if i > 1 else prompt_ids
142
+ max_context = 16384 - max_new_tokens - 1
143
+ if input_ids.shape[-1] > max_context:
144
+ input_ids = input_ids[:, -(max_context):]
145
+
146
+ with torch.inference_mode():
147
+ output_seq = model.generate(
148
+ input_ids=input_ids,
149
+ max_new_tokens=max_new_tokens,
150
+ do_sample=True,
151
+ top_p=top_p,
152
+ temperature=temperature,
153
+ repetition_penalty=repetition_penalty,
154
+ eos_token_id=mmtokenizer.eoa,
155
+ pad_token_id=mmtokenizer.eoa,
156
+ logits_processor=LogitsProcessorList([BlockTokenRangeProcessor(0, 32002)]),
157
+ num_beams=1
158
+ )
159
+
160
+ if i > 1:
161
+ raw_output = torch.cat([raw_output, prompt_ids, output_seq[:, input_ids.shape[-1]:]], dim=1)
162
+ else:
163
+ raw_output = output_seq
164
+ return raw_output
165
+
166
+ raw_output = generator()
167
+ ids = raw_output[0].cpu().numpy()
168
+ soa_idx = np.where(ids == mmtokenizer.soa)[0].tolist()
169
+ eoa_idx = np.where(ids == mmtokenizer.eoa)[0].tolist()
170
+
171
+ vocals, instrumentals = [], []
172
+ for i in range(len(soa_idx)):
173
+ codec_ids = ids[soa_idx[i] + 1:eoa_idx[i]]
174
+ codec_ids = codec_ids[:2 * (len(codec_ids) // 2)]
175
+ vocals_ids = codectool.ids2npy(rearrange(codec_ids, "(n b) -> b n", b=2)[0])
176
+ vocals.append(vocals_ids)
177
+ instrumentals_ids = codectool.ids2npy(rearrange(codec_ids, "(n b) -> b n", b=2)[1])
178
+ instrumentals.append(instrumentals_ids)
179
+
180
+ vocals = np.concatenate(vocals, axis=1)
181
+ instrumentals = np.concatenate(instrumentals, axis=1)
182
+
183
+ def save_audio(wav: torch.Tensor, path, sample_rate: int, rescale: bool = False):
184
+ limit = 0.99
185
+ max_val = wav.abs().max()
186
+ wav = wav * min(limit / max_val, 1) if rescale else wav.clamp(-limit, limit)
187
+ torchaudio.save(str(path), wav, sample_rate=sample_rate, encoding='PCM_S', bits_per_sample=16)
188
+
189
+ recons_output_dir = os.path.join(output_dir, "recons")
190
+ os.makedirs(recons_output_dir, exist_ok=True)
191
+ mix_stem = (vocals + instrumentals) / 2
192
+ mix_path = os.path.join(recons_output_dir, f"mix_{random_id}.wav")
193
+ save_audio(torch.tensor(mix_stem).unsqueeze(0), mix_path, 16000)
194
+ return mix_path
195
+
196
+ # ---------------- Gradio Interface ---------------- #
197
+
198
+ with gr.Blocks() as demo:
199
+ with gr.Column():
200
+ gr.Markdown("# 🎡 YuE: CPU Music Generator (Simplified Demo)")
201
+ with gr.Row():
202
+ with gr.Column():
203
+ genre_txt = gr.Textbox(label="Genre")
204
+ lyrics_txt = gr.Textbox(label="Lyrics")
205
+ submit_btn = gr.Button("Generate Song 🎢")
206
+ with gr.Column():
207
+ music_out = gr.Audio(label="Generated Music")
208
+
209
+ submit_btn.click(
210
+ fn=generate_music,
211
+ inputs=[genre_txt, lyrics_txt],
212
+ outputs=[music_out]
213
+ )
214
 
215
+ demo.queue().launch(show_error=True, share=True)