1littlecoder commited on
Commit
e39102f
·
verified ·
1 Parent(s): 5a1209e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +172 -0
app.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from matplotlib.animation import FuncAnimation
5
+ import tempfile
6
+ import os
7
+ import shutil
8
+ import subprocess
9
+ from typing import Any
10
+ import PIL
11
+ import processing_utils # Import or define your custom processing utilities
12
+
13
+ def make_waveform(
14
+ audio: tuple[int, np.ndarray],
15
+ bg_color: str = "#f3f4f6",
16
+ bg_image: str | None = None,
17
+ fg_alpha: float = 0.75,
18
+ bars_color: str | tuple[str, str] = ("#fbbf24", "#ea580c"),
19
+ bar_count: int = 50,
20
+ bar_width: float = 0.6,
21
+ animate: bool = False,
22
+ ) -> str:
23
+ if isinstance(audio, str):
24
+ audio_file = audio
25
+ audio = processing_utils.audio_from_file(audio)
26
+ else:
27
+ tmp_wav = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
28
+ processing_utils.audio_to_file(audio[0], audio[1], tmp_wav.name, format="wav")
29
+ audio_file = tmp_wav.name
30
+
31
+ if not os.path.isfile(audio_file):
32
+ raise ValueError("Audio file not found.")
33
+
34
+ ffmpeg = shutil.which("ffmpeg")
35
+ if not ffmpeg:
36
+ raise RuntimeError("ffmpeg not found.")
37
+
38
+ duration = round(len(audio[1]) / audio[0], 4)
39
+
40
+ def hex_to_rgb(hex_str):
41
+ return [int(hex_str[i : i + 2], 16) for i in range(1, 6, 2)]
42
+
43
+ def get_color_gradient(c1, c2, n):
44
+ if n < 1:
45
+ raise ValueError("Must have at least one stop in gradient")
46
+ c1_rgb = np.array(hex_to_rgb(c1)) / 255
47
+ c2_rgb = np.array(hex_to_rgb(c2)) / 255
48
+ mix_pcts = [x / (n - 1) for x in range(n)]
49
+ rgb_colors = [((1 - mix) * c1_rgb + (mix * c2_rgb)) for mix in mix_pcts]
50
+ return [
51
+ "#" + "".join(f"{int(round(val * 255)):02x}" for val in item)
52
+ for item in rgb_colors
53
+ ]
54
+
55
+ samples = audio[1]
56
+ if len(samples.shape) > 1:
57
+ samples = np.mean(samples, 1)
58
+ bins_to_pad = bar_count - (len(samples) % bar_count)
59
+ samples = np.pad(samples, [(0, bins_to_pad)])
60
+ samples = np.reshape(samples, (bar_count, -1))
61
+ samples = np.abs(samples)
62
+ samples = np.max(samples, 1)
63
+
64
+ color = (
65
+ bars_color
66
+ if isinstance(bars_color, str)
67
+ else get_color_gradient(bars_color[0], bars_color[1], bar_count)
68
+ )
69
+
70
+ fig = plt.figure(figsize=(5, 1), dpi=200, frameon=False)
71
+ plt.axis("off")
72
+ plt.margins(x=0)
73
+
74
+ bar_alpha = fg_alpha if animate else 1.0
75
+ barcollection = plt.bar(
76
+ np.arange(0, bar_count),
77
+ samples * 2,
78
+ bottom=(-1 * samples),
79
+ width=bar_width,
80
+ color=color,
81
+ alpha=bar_alpha,
82
+ )
83
+
84
+ tmp_img = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
85
+ savefig_kwargs: dict[str, Any] = {"bbox_inches": "tight"}
86
+ if bg_image is not None:
87
+ savefig_kwargs["transparent"] = True
88
+ else:
89
+ savefig_kwargs["facecolor"] = bg_color
90
+ plt.savefig(tmp_img.name, **savefig_kwargs)
91
+
92
+ if not animate:
93
+ waveform_img = PIL.Image.open(tmp_img.name)
94
+ waveform_img.save(tmp_img.name)
95
+ else:
96
+ def _animate(_):
97
+ for idx, b in enumerate(barcollection):
98
+ rand_height = np.random.uniform(0.8, 1.2)
99
+ b.set_height(samples[idx] * rand_height * 2)
100
+ b.set_y((-rand_height * samples)[idx])
101
+
102
+ frames = int(duration * 10)
103
+ anim = FuncAnimation(
104
+ fig,
105
+ _animate,
106
+ repeat=False,
107
+ blit=False,
108
+ frames=frames,
109
+ interval=100,
110
+ )
111
+ anim.save(tmp_img.name, writer="pillow", fps=10, codec="png", savefig_kwargs=savefig_kwargs)
112
+
113
+ output_mp4 = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
114
+
115
+ ffmpeg_cmd = [
116
+ ffmpeg,
117
+ "-loop",
118
+ "1",
119
+ "-i",
120
+ tmp_img.name,
121
+ "-i",
122
+ audio_file,
123
+ "-vf",
124
+ f"color=c=#FFFFFF77:s=1000x400[bar];[0][bar]overlay=-w+(w/{duration})*t:H-h:shortest=1",
125
+ "-t",
126
+ str(duration),
127
+ "-y",
128
+ output_mp4.name,
129
+ ]
130
+
131
+ subprocess.check_call(ffmpeg_cmd)
132
+ return output_mp4.name
133
+
134
+ # Gradio app
135
+ def generate_waveform(audio, bg_color, fg_alpha, bars_color, bar_count, bar_width, animate):
136
+ try:
137
+ video_path = make_waveform(
138
+ audio=(audio[0], np.array(audio[1])),
139
+ bg_color=bg_color,
140
+ fg_alpha=fg_alpha,
141
+ bars_color=bars_color,
142
+ bar_count=bar_count,
143
+ bar_width=bar_width,
144
+ animate=animate
145
+ )
146
+ return video_path
147
+ except Exception as e:
148
+ return str(e)
149
+
150
+ with gr.Blocks() as demo:
151
+ gr.Markdown("### Audio Waveform Generator")
152
+
153
+ with gr.Row():
154
+ audio_input = gr.Audio(label="Upload Audio", source="upload", type="numpy")
155
+ video_output = gr.Video(label="Waveform Video")
156
+
157
+ with gr.Row():
158
+ bg_color = gr.ColorPicker(label="Background Color", value="#f3f4f6")
159
+ fg_alpha = gr.Slider(label="Foreground Opacity", minimum=0.1, maximum=1.0, value=0.75)
160
+ bar_count = gr.Slider(label="Number of Bars", minimum=10, maximum=100, step=1, value=50)
161
+ bar_width = gr.Slider(label="Bar Width", minimum=0.1, maximum=1.0, value=0.6)
162
+ bars_color = gr.ColorPicker(label="Bars Color", value="#fbbf24")
163
+ animate = gr.Checkbox(label="Animate", value=False)
164
+
165
+ generate_button = gr.Button("Generate Waveform")
166
+ generate_button.click(
167
+ generate_waveform,
168
+ inputs=[audio_input, bg_color, fg_alpha, bars_color, bar_count, bar_width, animate],
169
+ outputs=video_output
170
+ )
171
+
172
+ demo.launch(debug = True)