Source code for jdxi_editor.ui.image.waveform
"""
waveform_icons
This module provides functions to generate PNG images representing different waveform icons
using the Python Imaging Library (PIL). Each function returns address base64-encoded string of
the generated image.
Functions:
generate_waveform_icon(icon_type, foreground_color, icon_scale): Generates address
- triangle: Generates address triangle waveform icon.
- upsaw: Generates an upward sawtooth waveform icon.
- square: Generates address square waveform icon.
- sine: Generates address sine waveform icon.
- noise: Generates address noise waveform icon.
- spsaw: Generates address special sawtooth waveform icon.
- pcm: Generates address PCM waveform icon.
- adsr: Generates an ADSR envelope waveform icon.
"""
import base64
import math
from io import BytesIO
from PIL import Image, ImageColor, ImageDraw
[docs]
def generate_waveform_icon(
waveform: str, foreground_color: str, icon_scale: float
) -> str:
"""
Generate address waveform icon as address base64-encoded PNG image
:param waveform: str
:param foreground_color: str
:param icon_scale: float
:return: icon
:rtype: str
"""
x = int(17 * icon_scale)
y = int(9 * icon_scale)
th = int(icon_scale + 0.49)
im = Image.new("RGBA", (x, y), (255, 255, 255, 0))
draw = ImageDraw.Draw(im)
color = ImageColor.getrgb(foreground_color)
half_y = y * 0.5
quarter_x = x * 0.25
three_quarters_x = x * 0.75
if waveform == "triangle":
draw.line(
[(0, half_y), (quarter_x, 0), (three_quarters_x, y - 1), (x, half_y)],
fill=color,
width=th,
)
elif waveform == "upsaw":
draw.line(
[(0, y - 1), (x * 0.5, 0), (x * 0.5, y - 1), (x - 1, 0)],
fill=color,
width=th,
)
elif waveform == "square":
draw.line(
[
(th * 0.5, y - 1),
(th * 0.5, 0),
(x * 0.5, 0),
(x * 0.5, y - 1),
(x - th * 0.5, y - 1),
(x - th * 0.5, 0),
],
fill=color,
width=th,
)
elif waveform == "sine":
# Define the number of points for smoothness
num_points = 60
sine_wave = [
(
i * x / (num_points - 1),
half_y + (math.sin(i * 2 * math.pi / (num_points - 1)) * half_y * 0.8),
)
for i in range(num_points)
]
draw.line(sine_wave, fill=color, width=th)
elif waveform == "noise":
import random
points = [
(th * 0.5 + x * 0.0588 * i, y * (0.5 + random.uniform(-0.4, 0.4)))
for i in range(16)
]
draw.line(points, fill=color, width=th)
elif waveform == "spsaw":
draw.line(
[(0, half_y), (y * 0.5, 0), (y * 0.5, y - 1), (x - 1, half_y)],
fill=color,
width=th,
)
elif waveform == "pcm":
for i in range(12):
draw.line(
[
(x * (0.1 * i), y),
(x * (0.1 * i), y - (y * (0.4 + 0.2 * (-1) ** i))),
],
fill=color,
width=th,
)
elif waveform == "pwsqu":
draw.line([(th * 0.5, y - 1), (th * 0.5, 0)], fill=color, width=th)
draw.line(
[(0, th * 0.5), (x * 0.68 - th * 0.5, th * 0.5)], fill=color, width=th
)
draw.line([(x * 0.68, 0), (x * 0.68, y - 1)], fill=color, width=th)
draw.line(
[(x * 0.68, y - th * 0.5), (x - 1, y - th * 0.5)], fill=color, width=th
)
draw.line([(x - th * 0.5, y - 1), (x - th * 0.5, 0)], fill=color, width=th)
elif waveform == "adsr":
# rgb = tuple(int(foreground_color[i : i + 2], 16) for i in (1, 3, 5))
width = int(17 * icon_scale)
height = int(9 * icon_scale)
# th = int(icon_scale + 0.49)
line_color = foreground_color
im = Image.new("RGBA", (width, height), (0, 0, 0, 0))
draw = ImageDraw.Draw(im)
# Define the ADSR shape
points = [
(0.2 * width, height * 1), # Start
(0.3 * width, height * 0), # Attack
(0.5 * width, height * 0.4), # Decay
(0.7 * width, height * 0.4), # Sustain
(0.9 * width, height * 1), # Release
]
# Draw the ADSR shape
draw.line(points, fill=line_color, width=3)
buffer = BytesIO()
im.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode("utf-8")