{"spec_id":"waveform-audio","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nwaveform-audio: Audio Waveform Plot\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-03\n\"\"\"\n\nimport os\n\nimport matplotlib.patheffects as pe\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as ticker\nimport numpy as np\n\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\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 categorical palette\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\nBRAND = IMPRINT_PALETTE[0]  # #009E73 — always first series\n\n# Data: simulated seismic recording — P-wave and S-wave arrivals\nnp.random.seed(42)\nsample_rate = 1000  # Hz\nduration = 10.0  # seconds\nnum_samples = int(sample_rate * duration)\ntime = np.linspace(0, duration, num_samples)\n\nsignal = np.random.normal(0, 0.015, num_samples)  # background microseismic noise\n\n# P-wave arrival at t=2s: ~10 Hz, moderate amplitude, 2 s duration\np_start = int(2.0 * sample_rate)\np_len = int(2.0 * sample_rate)\np_env = np.concatenate(\n    [np.linspace(0, 1, int(0.15 * p_len)), np.ones(int(0.35 * p_len)), np.linspace(1, 0.08, p_len - int(0.50 * p_len))]\n)\np_t = np.arange(p_len) / sample_rate\nsignal[p_start : p_start + p_len] += 0.32 * p_env * np.sin(2 * np.pi * 10 * p_t)\nsignal[p_start : p_start + p_len] += np.random.normal(0, 0.025, p_len)\n\n# S-wave arrival at t=4s: ~3 Hz, high amplitude, 4 s duration\ns_start = int(4.0 * sample_rate)\ns_len = int(4.0 * sample_rate)\ns_env = np.concatenate(\n    [np.linspace(0, 1, int(0.10 * s_len)), np.ones(int(0.40 * s_len)), np.linspace(1, 0.12, s_len - int(0.50 * s_len))]\n)\ns_t = np.arange(s_len) / sample_rate\nsignal[s_start : s_start + s_len] += 0.88 * s_env * np.sin(2 * np.pi * 3 * s_t)\nsignal[s_start : s_start + s_len] += np.random.normal(0, 0.03, s_len)\n\n# Coda (8–10 s): decaying mixed frequencies\nc_start = int(8.0 * sample_rate)\nc_len = int(2.0 * sample_rate)\nc_env = np.linspace(0.12, 0.02, c_len)\nc_t = np.arange(c_len) / sample_rate\nsignal[c_start : c_start + c_len] += c_env * (0.6 * np.sin(2 * np.pi * 3 * c_t) + 0.4 * np.sin(2 * np.pi * 8 * c_t))\n\namplitude = signal / np.abs(signal).max()  # normalise to [-1, 1]\n\n# Min/max envelope for dense rendering — avoids aliasing at plot resolution\nchunk_size = 40  # 1000 Hz / 40 = 25 envelope points per second\nnum_chunks = num_samples // chunk_size\namp_chunks = amplitude[: num_chunks * chunk_size].reshape(num_chunks, chunk_size)\ntime_chunks = time[: num_chunks * chunk_size].reshape(num_chunks, chunk_size)\nenv_max = amp_chunks.max(axis=1)\nenv_min = amp_chunks.min(axis=1)\nenv_time = time_chunks.mean(axis=1)\n\n# Plot\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Phase region backgrounds — Imprint palette, deuteranopia-safe (no red/green pair)\nphases = [\n    (0, 2, \"Background\\nNoise\", INK_MUTED),\n    (2, 4, \"P-wave\", IMPRINT_PALETTE[2]),  # blue  #4467A3\n    (4, 8, \"S-wave\", IMPRINT_PALETTE[1]),  # lavender #C475FD\n    (8, 10, \"Coda\", IMPRINT_PALETTE[3]),  # ochre #BD8233\n]\nfor t0, t1, label, clr in phases:\n    ax.axvspan(t0, t1, alpha=0.07, color=clr, zorder=0)\n    ax.text(\n        (t0 + t1) / 2,\n        0.97,\n        label,\n        ha=\"center\",\n        va=\"top\",\n        fontsize=6,\n        fontweight=\"semibold\",\n        color=clr,\n        alpha=0.95,\n        transform=ax.get_xaxis_transform(),\n        path_effects=[pe.withStroke(linewidth=1.5, foreground=PAGE_BG)],\n    )\n\n# Waveform: filled min/max envelope around zero baseline\nax.fill_between(env_time, env_max, env_min, color=BRAND, alpha=0.5, linewidth=0, zorder=2)\nax.plot(env_time, env_max, color=BRAND, linewidth=0.8, alpha=0.75, zorder=3)\nax.plot(env_time, env_min, color=BRAND, linewidth=0.8, alpha=0.75, zorder=3)\n\n# Zero reference line\nax.axhline(y=0, color=INK_SOFT, linewidth=0.8, alpha=0.5, zorder=1)\n\n# P and S arrival markers\nfor t_arr, _lbl, clr in [(2, \"P\", IMPRINT_PALETTE[2]), (4, \"S\", IMPRINT_PALETTE[1])]:\n    ax.axvline(x=t_arr, color=clr, linewidth=1.0, linestyle=\"--\", alpha=0.65, zorder=4)\n\n# Style\ntitle = \"waveform-audio · python · matplotlib · anyplot.ai\"\ntitle_fontsize = max(8, round(12 * 67 / len(title))) if len(title) > 67 else 12\nax.set_title(title, fontsize=title_fontsize, fontweight=\"medium\", color=INK, pad=8)\nax.set_xlabel(\"Time (s)\", fontsize=10, color=INK)\nax.set_ylabel(\"Amplitude\", fontsize=10, color=INK)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\nax.set_ylim(-1.08, 1.08)\nax.set_xlim(0, duration)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK)\nax.xaxis.set_major_locator(ticker.MultipleLocator(2))\nax.xaxis.set_minor_locator(ticker.MultipleLocator(0.5))\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}