JacobLinCool commited on
Commit
4335d4f
·
verified ·
1 Parent(s): fbe34db

Improve Space output layout and CJK chart fonts

Browse files
Files changed (4) hide show
  1. app.py +13 -5
  2. packages.txt +1 -0
  3. softchart/fonts.py +35 -0
  4. softchart/tja_image.py +8 -2
app.py CHANGED
@@ -16,6 +16,7 @@ import numpy as np
16
  import torch
17
 
18
  from softchart.generate import generate_song, generate_song_slot, load_hf
 
19
  from softchart.grid import debias_to_grid, fit_grid_fixed_bpm, fit_grid_piecewise
20
  from softchart.hf import SoftChartPlanner
21
  from softchart.rhythm import snap_chart
@@ -207,8 +208,15 @@ def write_tja(gen, bpm, title, course, level, wave, downbeats=None, grid_fit=Non
207
  def render_audio_plan(mel, title, course, plan=None):
208
  import matplotlib
209
  matplotlib.use("Agg")
 
210
  import matplotlib.pyplot as plt
211
 
 
 
 
 
 
 
212
  dur = mel.shape[1] / FPS
213
  fig = plt.figure(figsize=(13, 4.4 if plan else 3.2))
214
  gs = fig.add_gridspec(2 if plan else 1, 1,
@@ -341,7 +349,7 @@ def generate(audio, course, level, bpm_override, auto_plan_on, use_beat, use_pla
341
  grid_info = ""
342
  info = (f"BPM {bpm:.1f} · {len(g['hits'])} notes · {len(g['spans'])} spans" + grid_info
343
  + (f" · plan: {sum(1 for b in plan if b[3]==1)} gaps, {sum(1 for b in plan if b[3]==2)} climax" if plan else ""))
344
- return audio_plan_img, tja_img, tja_path, info
345
 
346
 
347
  with gr.Blocks(title="SoftChart — AI Taiko chart generator") as demo:
@@ -366,12 +374,12 @@ with gr.Blocks(title="SoftChart — AI Taiko chart generator") as demo:
366
  sampling = gr.Checkbox(label="Sampling (diverse) vs greedy (best)", value=False)
367
  btn = gr.Button("Generate chart", variant="primary")
368
  with gr.Column():
369
- out_img = gr.Image(label="Full-song mel + plan")
370
- out_chart = gr.Image(label="TJA chart")
371
- out_info = gr.Textbox(label="Result", interactive=False)
372
  out_tja = gr.File(label="Download .tja")
 
 
 
373
  btn.click(generate, [audio, course, level, bpm, auto_plan_on, use_beat, use_planner, sampling],
374
- [out_img, out_chart, out_tja, out_info])
375
 
376
  if __name__ == "__main__":
377
  # show_api=False avoids a gradio_client schema-introspection bug
 
16
  import torch
17
 
18
  from softchart.generate import generate_song, generate_song_slot, load_hf
19
+ from softchart.fonts import cjk_font_path
20
  from softchart.grid import debias_to_grid, fit_grid_fixed_bpm, fit_grid_piecewise
21
  from softchart.hf import SoftChartPlanner
22
  from softchart.rhythm import snap_chart
 
208
  def render_audio_plan(mel, title, course, plan=None):
209
  import matplotlib
210
  matplotlib.use("Agg")
211
+ from matplotlib import font_manager
212
  import matplotlib.pyplot as plt
213
 
214
+ font_path = cjk_font_path()
215
+ if font_path is not None:
216
+ font_manager.fontManager.addfont(font_path)
217
+ matplotlib.rcParams["font.family"] = font_manager.FontProperties(fname=font_path).get_name()
218
+ matplotlib.rcParams["axes.unicode_minus"] = False
219
+
220
  dur = mel.shape[1] / FPS
221
  fig = plt.figure(figsize=(13, 4.4 if plan else 3.2))
222
  gs = fig.add_gridspec(2 if plan else 1, 1,
 
349
  grid_info = ""
350
  info = (f"BPM {bpm:.1f} · {len(g['hits'])} notes · {len(g['spans'])} spans" + grid_info
351
  + (f" · plan: {sum(1 for b in plan if b[3]==1)} gaps, {sum(1 for b in plan if b[3]==2)} climax" if plan else ""))
352
+ return tja_path, info, tja_img, audio_plan_img
353
 
354
 
355
  with gr.Blocks(title="SoftChart — AI Taiko chart generator") as demo:
 
374
  sampling = gr.Checkbox(label="Sampling (diverse) vs greedy (best)", value=False)
375
  btn = gr.Button("Generate chart", variant="primary")
376
  with gr.Column():
 
 
 
377
  out_tja = gr.File(label="Download .tja")
378
+ out_info = gr.Textbox(label="Result", interactive=False)
379
+ out_chart = gr.Image(label="TJA chart")
380
+ out_img = gr.Image(label="Full-song mel + plan")
381
  btn.click(generate, [audio, course, level, bpm, auto_plan_on, use_beat, use_planner, sampling],
382
+ [out_tja, out_info, out_chart, out_img])
383
 
384
  if __name__ == "__main__":
385
  # show_api=False avoids a gradio_client schema-introspection bug
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ fonts-noto-cjk
softchart/fonts.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Font helpers for generated preview images."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+
8
+ def cjk_font_path(bold: bool = False) -> str | None:
9
+ names = (
10
+ (
11
+ "/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc",
12
+ "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
13
+ ),
14
+ (
15
+ "/usr/share/fonts/opentype/noto/NotoSansCJKjp-Bold.otf",
16
+ "/usr/share/fonts/opentype/noto/NotoSansCJKjp-Regular.otf",
17
+ ),
18
+ (
19
+ "/usr/share/fonts/truetype/noto/NotoSansCJK-Bold.ttc",
20
+ "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
21
+ ),
22
+ (
23
+ "/System/Library/Fonts/ヒラギノ角ゴシック W6.ttc",
24
+ "/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc",
25
+ ),
26
+ (
27
+ "/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
28
+ "/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
29
+ ),
30
+ )
31
+ for bold_path, regular_path in names:
32
+ path = bold_path if bold else regular_path
33
+ if os.path.exists(path):
34
+ return path
35
+ return None
softchart/tja_image.py CHANGED
@@ -16,6 +16,8 @@ from dataclasses import dataclass
16
 
17
  from PIL import Image, ImageDraw, ImageFont
18
 
 
 
19
 
20
  _NOTE_CHARS = set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
21
  _COURSE_NAME = {
@@ -35,7 +37,9 @@ class ParsedTJA:
35
 
36
 
37
  def _font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
 
38
  names = (
 
39
  "DejaVuSans-Bold.ttf" if bold else "DejaVuSans.ttf",
40
  "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" if bold else
41
  "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
@@ -45,6 +49,8 @@ def _font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont | ImageFont.I
45
  "/System/Library/Fonts/Supplemental/Helvetica.ttf",
46
  )
47
  for name in names:
 
 
48
  try:
49
  return ImageFont.truetype(name, size=size)
50
  except OSError:
@@ -186,14 +192,14 @@ def _draw_note(draw: ImageDraw.ImageDraw, x: int, y: int, symbol: str,
186
  else:
187
  fill = "#8d96a0"
188
 
189
- radius = 16 if symbol in {"3", "4", "6", "A", "B"} else 12
190
  if symbol == "8":
191
  draw.rectangle((x - 5, y - 12, x + 5, y + 12), fill=fill)
192
  return
193
 
194
  draw.ellipse((x - radius, y - radius, x + radius, y + radius),
195
  fill=fill, outline="#202020", width=2)
196
- draw.ellipse((x - radius + 4, y - radius + 4, x - radius + 9, y - radius + 9),
197
  fill="#ffffff")
198
  label = "B" if symbol in {"3", "4", "6", "A", "B"} else ("R" if symbol in {"5", "6"} else ("P" if symbol in {"7", "9"} else ""))
199
  if label:
 
16
 
17
  from PIL import Image, ImageDraw, ImageFont
18
 
19
+ from .fonts import cjk_font_path
20
+
21
 
22
  _NOTE_CHARS = set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
23
  _COURSE_NAME = {
 
37
 
38
 
39
  def _font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
40
+ cjk_path = cjk_font_path(bold=bold)
41
  names = (
42
+ cjk_path,
43
  "DejaVuSans-Bold.ttf" if bold else "DejaVuSans.ttf",
44
  "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" if bold else
45
  "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
 
49
  "/System/Library/Fonts/Supplemental/Helvetica.ttf",
50
  )
51
  for name in names:
52
+ if name is None:
53
+ continue
54
  try:
55
  return ImageFont.truetype(name, size=size)
56
  except OSError:
 
192
  else:
193
  fill = "#8d96a0"
194
 
195
+ radius = 14 if symbol in {"3", "4", "6", "A", "B"} else 10
196
  if symbol == "8":
197
  draw.rectangle((x - 5, y - 12, x + 5, y + 12), fill=fill)
198
  return
199
 
200
  draw.ellipse((x - radius, y - radius, x + radius, y + radius),
201
  fill=fill, outline="#202020", width=2)
202
+ draw.ellipse((x - radius + 3, y - radius + 3, x - radius + 8, y - radius + 8),
203
  fill="#ffffff")
204
  label = "B" if symbol in {"3", "4", "6", "A", "B"} else ("R" if symbol in {"5", "6"} else ("P" if symbol in {"7", "9"} else ""))
205
  if label: