{"spec_id":"waveform-audio","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nwaveform-audio: Audio Waveform Plot\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-03\n\"\"\"\n\nimport os\nimport sys\nimport time\nfrom pathlib import Path\n\n\n# Prevent this file (bokeh.py) from shadowing the installed bokeh package on direct invocation\n_impl_dir = os.path.abspath(os.path.dirname(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p or \".\") != _impl_dir]\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.models import BoxAnnotation, ColumnDataSource, Label, Range1d, Span\nfrom bokeh.plotting import figure\nfrom PIL import Image\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette phase colors (green → blue → cyan: cohesive cool progression)\nCOLOR_ATTACK = \"#009E73\"  # Imprint position 1, brand green\nCOLOR_SUSTAIN = \"#4467A3\"  # Imprint position 3, blue\nCOLOR_RELEASE = \"#2ABCCD\"  # Imprint position 6, cyan\n\n# Data\nnp.random.seed(42)\nsample_rate = 22050\nduration = 1.5\nnum_samples = int(sample_rate * duration)\nt = np.linspace(0, duration, num_samples)\n\n# Synthesize audio: fundamental + harmonics with amplitude envelope\nfundamental = 220\nsignal = (\n    0.6 * np.sin(2 * np.pi * fundamental * t)\n    + 0.25 * np.sin(2 * np.pi * fundamental * 2 * t)\n    + 0.1 * np.sin(2 * np.pi * fundamental * 3 * t)\n    + 0.05 * np.sin(2 * np.pi * fundamental * 5 * t)\n)\n\n# Amplitude envelope: attack-sustain-release shape\nenvelope = np.ones(num_samples)\nattack_samples = int(0.05 * sample_rate)\nrelease_samples = int(0.3 * sample_rate)\nenvelope[:attack_samples] = np.linspace(0, 1, attack_samples)\nenvelope[-release_samples:] = np.linspace(1, 0, release_samples)\n\n# Phase boundaries (in seconds)\nattack_end = 0.05\nsustain_end = duration - 0.3\n\n# Add tremolo modulation\ntremolo = 1.0 - 0.15 * np.sin(2 * np.pi * 5.5 * t)\namplitude = signal * envelope * tremolo\namplitude = amplitude / np.max(np.abs(amplitude))\n\n# Min/max envelope rendering (downsampled to avoid aliasing)\nchunk_size = 8\nnum_chunks = num_samples // chunk_size\nt_chunked = t[: num_chunks * chunk_size].reshape(num_chunks, chunk_size)\namp_chunked = amplitude[: num_chunks * chunk_size].reshape(num_chunks, chunk_size)\n\nenv_time = t_chunked.mean(axis=1)\nenv_max = amp_chunked.max(axis=1)\nenv_min = amp_chunked.min(axis=1)\n\n# Split into attack / sustain / release segments\nattack_mask = env_time <= attack_end\nsustain_mask = (env_time > attack_end) & (env_time <= sustain_end)\nrelease_mask = env_time > sustain_end\n\nsource_attack = ColumnDataSource(\n    data={\"x\": env_time[attack_mask], \"y1\": env_min[attack_mask], \"y2\": env_max[attack_mask]}\n)\nsource_sustain = ColumnDataSource(\n    data={\"x\": env_time[sustain_mask], \"y1\": env_min[sustain_mask], \"y2\": env_max[sustain_mask]}\n)\nsource_release = ColumnDataSource(\n    data={\"x\": env_time[release_mask], \"y1\": env_min[release_mask], \"y2\": env_max[release_mask]}\n)\n\n# Plot\ntitle = \"waveform-audio · python · bokeh · anyplot.ai\"\np = figure(\n    width=3200,\n    height=1800,\n    title=title,\n    x_axis_label=\"Time (seconds)\",\n    y_axis_label=\"Amplitude\",\n    y_range=Range1d(-1.12, 1.12),\n    background_fill_color=PAGE_BG,\n    toolbar_location=None,\n    min_border_bottom=160,\n    min_border_left=180,\n    min_border_top=110,\n    min_border_right=50,\n)\n\n# Phase region shading with BoxAnnotation\nphase_alpha = 0.09\np.add_layout(BoxAnnotation(left=0, right=attack_end, fill_color=COLOR_ATTACK, fill_alpha=phase_alpha))\np.add_layout(BoxAnnotation(left=attack_end, right=sustain_end, fill_color=COLOR_SUSTAIN, fill_alpha=phase_alpha))\np.add_layout(BoxAnnotation(left=sustain_end, right=duration, fill_color=COLOR_RELEASE, fill_alpha=phase_alpha))\n\n# Filled waveform using varea (idiomatic Bokeh)\np.varea(x=\"x\", y1=\"y1\", y2=\"y2\", source=source_attack, fill_color=COLOR_ATTACK, fill_alpha=0.45)\np.varea(x=\"x\", y1=\"y1\", y2=\"y2\", source=source_sustain, fill_color=COLOR_SUSTAIN, fill_alpha=0.40)\np.varea(x=\"x\", y1=\"y1\", y2=\"y2\", source=source_release, fill_color=COLOR_RELEASE, fill_alpha=0.45)\n\n# Waveform outline edges\nfor src, color in [(source_attack, COLOR_ATTACK), (source_sustain, COLOR_SUSTAIN), (source_release, COLOR_RELEASE)]:\n    p.line(\"x\", \"y2\", source=src, line_color=color, line_width=2.5, line_alpha=0.8)\n    p.line(\"x\", \"y1\", source=src, line_color=color, line_width=2.5, line_alpha=0.8)\n\n# Zero baseline\np.add_layout(Span(location=0, dimension=\"width\", line_color=INK_SOFT, line_width=2, line_alpha=0.5))\n\n# Phase labels — 28pt full-alpha, clearly visible over waveform\nlabel_props = {\"text_font_size\": \"28pt\", \"text_color\": INK_MUTED, \"text_font_style\": \"italic\", \"text_alpha\": 1.0}\np.add_layout(Label(x=attack_end / 2, y=0.92, text=\"Attack\", text_align=\"center\", **label_props))\np.add_layout(Label(x=(attack_end + sustain_end) / 2, y=0.92, text=\"Sustain\", text_align=\"center\", **label_props))\np.add_layout(Label(x=(sustain_end + duration) / 2, y=0.92, text=\"Release\", text_align=\"center\", **label_props))\n\n# Phase boundary lines\nfor boundary in [attack_end, sustain_end]:\n    p.add_layout(\n        Span(\n            location=boundary,\n            dimension=\"height\",\n            line_color=INK_MUTED,\n            line_width=2,\n            line_dash=\"dashed\",\n            line_alpha=0.5,\n        )\n    )\n\n# Style\np.title.text_font_size = \"50pt\"\np.title.text_font_style = \"normal\"\np.title.text_color = INK\np.xaxis.axis_label_text_font_size = \"42pt\"\np.yaxis.axis_label_text_font_size = \"42pt\"\np.xaxis.major_label_text_font_size = \"34pt\"\np.yaxis.major_label_text_font_size = \"34pt\"\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\np.xaxis.minor_tick_line_color = None\np.yaxis.minor_tick_line_color = None\np.outline_line_color = None\np.border_fill_color = PAGE_BG\n\np.xgrid.grid_line_color = None\np.ygrid.grid_line_color = INK\np.ygrid.grid_line_alpha = 0.15\n\np.yaxis.ticker = [-1.0, -0.5, 0.0, 0.5, 1.0]\n\n# Save HTML\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome (Selenium)\n# Window must exceed figure size so the full 3200×1800 canvas fits in the viewport\nW, H = 3200, 1800\nW_WIN, H_WIN = W + 200, H + 200\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W_WIN},{H_WIN}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W_WIN, H_WIN)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n\n# Crop to exact figure dimensions (window was oversized to avoid viewport clipping)\nimg = Image.open(f\"plot-{THEME}.png\")\nimg.crop((0, 0, W, H)).save(f\"plot-{THEME}.png\")\n"}