{"id":1323,"date":"2026-03-24T13:31:10","date_gmt":"2026-03-24T13:31:10","guid":{"rendered":"https:\/\/primetoolhub.com\/?page_id=1323"},"modified":"2026-07-06T11:50:37","modified_gmt":"2026-07-06T11:50:37","slug":"free-all-in-one-audio-editor-converter","status":"publish","type":"page","link":"https:\/\/schoolict.net\/tools\/free-all-in-one-audio-editor-converter\/","title":{"rendered":"Free All in One Audio Editor &amp; Converter"},"content":{"rendered":"\n<div class=\"pth-hero-section\">\n    <div class=\"pth-hero-content\">\n        <h2>Free All in One Audio Editor &amp; Converter<\/h2>\n        <p>Looking for a free online audio editor? Trim, reverse, convert to MP3, and boost volume instantly in your browser with our 100% offline audio studio.<\/p>\n\n        <div id=\"pth-toc-placeholder\"><\/div>\n    <\/div>\n\n  <div class=\"pth-hero-image\">\n    <img data-no-lazy=\"1\"\n         src=\"https:\/\/schoolict.net\/tools\/wp-content\/uploads\/2026\/03\/Free-All-in-One-Audio-Editor-800x447.jpeg\"\n         width=\"800\"\n         height=\"447\"\n         alt=\"All in One Audio Editor\"\n         fetchpriority=\"high\"\n         loading=\"eager\"\n         decoding=\"async\"\n         style=\"width:100%; height:auto; display:block;\">\n  <\/div>\n<\/div>\n\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Table of Contents<\/h2><nav><ul><li><a href=\"#\ud83d\udd34-how-f-fmpeg-wasm-runs-audio-processing-inside-your-browser\">\ud83d\udd34 How FFmpeg WASM Runs Audio Processing Inside Your Browser<\/a><\/li><li><a href=\"#\ud83d\udfe1-what-each-audio-tool-does-the-f-fmpeg-filter-behind-every-button\">\ud83d\udfe1 What Each Audio Tool Does \u2014 The FFmpeg Filter Behind Every Button<\/a><ul><li><a href=\"#\ud83d\udfe2-convert-trim-and-volume-the-foundation-tools\">\ud83d\udfe2 Convert, Trim, and Volume \u2014 The Foundation Tools<\/a><\/li><li><a href=\"#\ud83d\udfe2-effects-tools-normalize-eq-spatial-and-voice-changers\">\ud83d\udfe2 Effects Tools \u2014 Normalize, EQ, Spatial, and Voice Changers<\/a><\/li><\/ul><\/li><li><a href=\"#\ud83d\udfe1-choosing-the-right-output-format-mp-3-wav-or-ogg\">\ud83d\udfe1 Choosing the Right Output Format \u2014 MP3, WAV, or OGG<\/a><ul><li><a href=\"#\ud83d\udfe2-tool-search-load-sample-and-reset-studio\">\ud83d\udfe2 Tool Search, Load Sample, and Reset Studio<\/a><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n\n\n<!-- LSCACHE_DISABLE -->\r\n<div id=\"pth-as-wrapper\">\r\n\r\n<style data-no-optimize=\"1\">\r\n#pth-as-wrapper *,#pth-as-wrapper *::before,#pth-as-wrapper *::after{box-sizing:border-box;min-width:0;}\r\n#pth-as-wrapper{font-family:'Inter',system-ui,sans-serif;background:#f8fafc;border-radius:16px;padding:20px;color:#1e293b;font-weight:500;opacity:1;display:block;}\r\n.pth-as-header{margin-bottom:16px;}\r\n.pth-as-meta-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px;}\r\n.pth-as-version-badge{background:#eff6ff;color:#1d4ed8;font-size:.72rem;font-weight:700;padding:3px 10px;border-radius:20px;border:1px solid #bfdbfe;}\r\n.pth-as-status-offline{background:#f0fdf4;color:#047857;font-size:.72rem;font-weight:700;padding:3px 10px;border-radius:20px;border:1px solid #bbf7d0;}\r\n.pth-as-header h2{color:#1d4ed8;font-size:1.5rem;font-weight:800;margin:0 0 6px;}\r\n.pth-as-header p{color:#334155;font-size:.88rem;margin:0;font-weight:500;}\r\n.pth-as-tabs{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px;border-bottom:2px solid #e2e8f0;padding-bottom:10px;}\r\n.pth-as-tab-btn{background:#fff;border:2px solid #e2e8f0;color:#334155;font-size:.85rem;font-weight:700;padding:9px 16px;border-radius:8px;cursor:pointer;font-family:'Inter',system-ui,sans-serif;white-space:nowrap;transition:all .15s;}\r\n.pth-as-tab-btn:hover{border-color:#2563eb;color:#1d4ed8;background:#eff6ff;}\r\n.pth-as-tab-btn.pth-as-active{background:#2563eb;border-color:#2563eb;color:#fff;}\r\n.pth-as-panel{display:none;opacity:1;}\r\n.pth-as-panel.pth-as-panel-active{display:block;}\r\n.pth-as-empty{background:#fff;border:1px dashed #cbd5e1;border-radius:12px;padding:30px 20px;text-align:center;color:#334155;font-weight:600;min-height:120px;}\r\n@media(max-width:768px){#pth-as-wrapper{padding:10px;border-radius:0;background:transparent;}.pth-as-tab-btn{font-size:.78rem;padding:7px 12px;}.pth-as-header h2{font-size:1.2rem;}}\r\n<\/style>\r\n\r\n<div class=\"pth-as-header\">\r\n  <div class=\"pth-as-meta-bar\">\r\n    <span class=\"pth-as-version-badge\">v1.0<\/span>\r\n    <span class=\"pth-as-status-offline\">\ud83d\udd12 100% Offline<\/span>\r\n  <\/div>\r\n  <h2>\ud83c\udfa7 Audio Studio<\/h2>\r\n  <p>Trim, convert and add effects, plus noise removal and vocal cutting \u2014 all in your browser. No upload, no login.<\/p>\r\n<\/div>\r\n\r\n<div class=\"pth-as-tabs\" role=\"tablist\" aria-label=\"Audio Studio tools\">\r\n  <button class=\"pth-as-tab-btn pth-as-active\" data-tab=\"audio\" role=\"tab\" aria-selected=\"true\">\ud83c\udfb5 Audio Studio<\/button>\r\n  <button class=\"pth-as-tab-btn\"               data-tab=\"voice\" role=\"tab\" aria-selected=\"false\">\ud83c\udf99\ufe0f Noise &amp; Voice<\/button>\r\n<\/div>\r\n\r\n<div class=\"pth-as-panel pth-as-panel-active\" id=\"pth-as-tab-audio\" role=\"tabpanel\">\r\n  <div class=\"pth-as-empty\" id=\"pth-as-empty-audio\">Loading Audio Studio\u2026<\/div>\r\n<\/div>\r\n<div class=\"pth-as-panel\" id=\"pth-as-tab-voice\" role=\"tabpanel\">\r\n  <div class=\"pth-as-empty\" id=\"pth-as-empty-voice\">Loading Noise &amp; Voice tools\u2026<\/div>\r\n<\/div>\r\n\r\n<\/div>\r\n<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n  'use strict';\r\n  window.pthAS = window.pthAS || {};\r\n  window.pthAS.activeTab = 'audio';\r\n\r\n  function initTabs() {\r\n    var root = document.getElementById('pth-as-wrapper');\r\n    if (!root) return;\r\n    var btns = root.querySelectorAll('.pth-as-tab-btn');\r\n    btns.forEach(function (btn) {\r\n      btn.addEventListener('click', function () {\r\n        var tab = btn.getAttribute('data-tab');\r\n        btns.forEach(function (b) {\r\n          b.classList.remove('pth-as-active');\r\n          b.setAttribute('aria-selected', 'false');\r\n        });\r\n        btn.classList.add('pth-as-active');\r\n        btn.setAttribute('aria-selected', 'true');\r\n        root.querySelectorAll('.pth-as-panel').forEach(function (p) {\r\n          p.classList.remove('pth-as-panel-active');\r\n        });\r\n        var panel = document.getElementById('pth-as-tab-' + tab);\r\n        if (panel) panel.classList.add('pth-as-panel-active');\r\n        window.pthAS.activeTab = tab;\r\n        document.dispatchEvent(new CustomEvent('pthASTabActive', { detail: { tab: tab } }));\r\n      });\r\n    });\r\n\r\n    \/* Deep-link: ?tab=audio \/ ?tab=voice *\/\r\n    try {\r\n      var want = new URLSearchParams(window.location.search).get('tab');\r\n      if (want === 'audio' || want === 'voice') {\r\n        var target = root.querySelector('.pth-as-tab-btn[data-tab=\"' + want + '\"]');\r\n        if (target) target.click();\r\n      }\r\n    } catch (e) {}\r\n  }\r\n\r\n  function runInit() {\r\n    if (window.pthAS.shellReady) return;\r\n    if (!document.getElementById('pth-as-wrapper')) return;\r\n    window.pthAS.shellReady = true;\r\n    initTabs();\r\n  }\r\n\r\n  runInit();\r\n  if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', runInit); }\r\n  window.addEventListener('load', function () { setTimeout(runInit, 50); });\r\n  if (!window.pthAS.shellReady && typeof MutationObserver !== 'undefined') {\r\n    var obs = new MutationObserver(function () {\r\n      if (document.getElementById('pth-as-wrapper')) { obs.disconnect(); runInit(); }\r\n    });\r\n    obs.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n    setTimeout(function () { obs.disconnect(); }, 10000);\r\n  }\r\n})();\r\n<\/script>\n\n\n<style>\r\n\/* ============================================================\r\n   PTH AUDIO STUDIO PRO \u2014 v4.0\r\n   AdSense + PageSpeed + WCAG 2.1 AA + WP\/LiteSpeed Safe\r\n   FFmpeg WASM \u2014 loaded via separate header Woody snippet\r\n   ============================================================ *\/\r\n\r\n#pth-audio-wrapper {\r\n    font-family: 'Inter', system-ui, sans-serif;\r\n    width: 100%; max-width: 1400px; margin: 0 auto;\r\n    box-sizing: border-box; background: #f8fafc;\r\n    padding: 20px; border-radius: 16px;\r\n    color: #1e293b; font-weight: 500;\r\n}\r\n#pth-audio-wrapper * { box-sizing: border-box; min-width: 0; }\r\n#pth-audio-wrapper h2,\r\n#pth-audio-wrapper h3,\r\n#pth-audio-wrapper h4 { color: #0f172a; font-weight: 800; margin-top: 0; }\r\n\r\n\/* \u2500\u2500 Tool Title \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-tool-title {\r\n    font-size: 26px; font-weight: 800; color: #1d4ed8; margin: 0;\r\n}\r\n\r\n\/* \u2500\u2500 Cards \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-card {\r\n    background: #ffffff; border: 1px solid #e2e8f0;\r\n    border-radius: 12px; padding: 24px; margin-bottom: 20px;\r\n    box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05);\r\n}\r\n\r\n\/* \u2500\u2500 Meta bar \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-tool-meta-bar {\r\n    display: flex; align-items: center; gap: 10px;\r\n    flex-wrap: wrap; margin-bottom: 16px;\r\n}\r\n#pth-audio-wrapper .pth-version-badge {\r\n    background: #eff6ff; color: #1d4ed8; border: 1px solid #bfdbfe;\r\n    font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 4px;\r\n}\r\n#pth-audio-wrapper .pth-status-offline {\r\n    background: #f0fdf4; color: #15803d; border: 1px solid #bbf7d0;\r\n    font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 4px;\r\n}\r\n#pth-audio-wrapper .pth-engine-badge {\r\n    font-size: 11px; font-weight: 700; padding: 2px 10px;\r\n    border-radius: 4px; background: #fef9c3; color: #854d0e;\r\n    border: 1px solid #fde68a; transition: 0.3s;\r\n}\r\n#pth-audio-wrapper .pth-engine-badge.ready {\r\n    background: #f0fdf4; color: #15803d; border-color: #bbf7d0;\r\n}\r\n\/* \u2500\u2500 Engine loading progress bar \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-engine-load-wrap {\r\n    display: none; background: #f5f3ff; border: 1px solid #ddd6fe;\r\n    border-radius: 10px; padding: 14px 16px; margin-bottom: 16px;\r\n}\r\n#pth-audio-wrapper .pth-engine-load-wrap.pth-visible { display: block; }\r\n#pth-audio-wrapper .pth-engine-load-label {\r\n    font-size: 12px; font-weight: 700; color: #6d28d9;\r\n    margin-bottom: 8px; display: flex; justify-content: space-between;\r\n    align-items: center;\r\n}\r\n#pth-audio-wrapper .pth-engine-load-pct {\r\n    font-size: 12px; font-weight: 800; color: #5b21b6;\r\n}\r\n#pth-audio-wrapper .pth-engine-load-bar-bg {\r\n    background: #ede9fe; border-radius: 6px; height: 8px; overflow: hidden;\r\n}\r\n#pth-audio-wrapper .pth-engine-load-bar {\r\n    height: 100%; width: 0%; background: linear-gradient(90deg, #7c3aed, #2563eb);\r\n    border-radius: 6px; transition: width 0.4s ease;\r\n}\r\n#pth-audio-wrapper .pth-engine-load-status {\r\n    font-size: 11px; color: #4c1d95; margin-top: 6px;\r\n    font-weight: 700;\r\n}\r\n\r\n\/* \u2500\u2500 Layout \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-flex-layout {\r\n    display: grid; grid-template-columns: 1.8fr 1fr;\r\n    gap: 24px; align-items: start;\r\n}\r\n#pth-audio-wrapper .pth-flex-layout.pth-full-width {\r\n    grid-template-columns: 100%;\r\n}\r\n\r\n\/* \u2500\u2500 Buttons \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-btn {\r\n    padding: 10px 20px; border-radius: 8px; font-weight: 700;\r\n    cursor: pointer; border: none; font-family: 'Inter', sans-serif;\r\n    font-size: 13px; transition: 0.2s; display: inline-flex;\r\n    align-items: center; justify-content: center; gap: 6px;\r\n}\r\n#pth-audio-wrapper .pth-btn-primary { background: #2563eb; color: #fff; }\r\n#pth-audio-wrapper .pth-btn-primary:hover { background: #1d4ed8; }\r\n#pth-audio-wrapper .pth-btn-secondary {\r\n    background: #f1f5f9; color: #0f172a; border: 1px solid #e2e8f0;\r\n}\r\n#pth-audio-wrapper .pth-btn-secondary:hover { background: #e2e8f0; }\r\n#pth-audio-wrapper .pth-btn-danger {\r\n    background: #fef2f2; color: #991b1b; border: 1px solid #fecaca;\r\n}\r\n#pth-audio-wrapper .pth-btn-danger:hover { background: #fee2e2; }\r\n#pth-audio-wrapper .pth-btn-success { background: #059669; color: #fff; }\r\n#pth-audio-wrapper .pth-btn-success:hover { background: #047857; }\r\n#pth-audio-wrapper .pth-btn-purple { background: #7c3aed; color: #fff; }\r\n#pth-audio-wrapper .pth-btn-purple:hover { background: #6d28d9; }\r\n#pth-audio-wrapper .pth-sample-btn {\r\n    background: #d1fae5; color: #047857; border: 1px solid #10b981;\r\n    font-size: 0.75rem; padding: 4px 10px; border-radius: 6px;\r\n    font-weight: 700; cursor: pointer; transition: 0.2s;\r\n    display: inline-flex; align-items: center; gap: 4px;\r\n}\r\n#pth-audio-wrapper .pth-sample-btn:hover { background: #a7f3d0; }\r\n#pth-audio-wrapper .pth-btn:disabled,\r\n#pth-audio-wrapper .pth-process-btn:disabled {\r\n    opacity: 0.5; cursor: not-allowed; pointer-events: none;\r\n}\r\n\r\n\/* \u2500\u2500 Upload area \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-upload-zone {\r\n    border: 2px dashed #c4b5fd; border-radius: 12px;\r\n    background: #f5f3ff; padding: 32px 20px;\r\n    text-align: center; transition: 0.2s; margin-bottom: 20px;\r\n}\r\n#pth-audio-wrapper .pth-upload-zone.has-file {\r\n    border-color: #10b981; background: #f0fdf4;\r\n}\r\n#pth-audio-wrapper .pth-upload-icon { font-size: 40px; margin-bottom: 10px; display: block; }\r\n#pth-audio-wrapper .pth-upload-label {\r\n    background: #7c3aed; color: #fff; padding: 12px 32px;\r\n    border-radius: 10px; font-size: 15px; font-weight: 700;\r\n    display: inline-flex; align-items: center; gap: 8px;\r\n    cursor: pointer; transition: 0.2s;\r\n    box-shadow: 0 4px 14px rgba(124,58,237,0.3);\r\n}\r\n#pth-audio-wrapper .pth-upload-label:hover { background: #6d28d9; transform: translateY(-2px); }\r\n#pth-audio-wrapper .pth-upload-label.pth-disabled {\r\n    background: #94a3b8; cursor: not-allowed;\r\n    pointer-events: none; box-shadow: none;\r\n}\r\n#pth-audio-wrapper .pth-file-info {\r\n    display: none; margin-top: 12px; font-size: 13px;\r\n    color: #047857; font-weight: 700;\r\n}\r\n#pth-audio-wrapper .pth-format-hint {\r\n    font-size: 11px; color: #64748b; margin-top: 8px; font-weight: 500;\r\n}\r\n\r\n\/* \u2500\u2500 Audio players grid \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-players-grid {\r\n    display: grid; grid-template-columns: 1fr 1fr;\r\n    gap: 16px; margin-bottom: 20px;\r\n}\r\n#pth-audio-wrapper .pth-player-box {\r\n    background: #f8fafc; border: 1px solid #e2e8f0;\r\n    border-radius: 10px; padding: 16px;\r\n    display: flex; flex-direction: column; min-height: 140px;\r\n}\r\n#pth-audio-wrapper .pth-player-label {\r\n    font-size: 12px; font-weight: 800; color: #64748b;\r\n    text-transform: uppercase; letter-spacing: 0.5px;\r\n    margin-bottom: 10px; display: flex;\r\n    justify-content: space-between; align-items: center;\r\n}\r\n#pth-audio-wrapper .pth-player-size {\r\n    font-size: 11px; font-weight: 700; color: #5b21b6;\r\n}\r\n#pth-audio-wrapper .pth-player-size.out { color: #059669; }\r\n#pth-audio-wrapper .pth-audio-el {\r\n    width: 100%; display: none; margin-top: auto;\r\n}\r\n#pth-audio-wrapper .pth-player-ph {\r\n    display: flex; flex-direction: column; align-items: center;\r\n    justify-content: center; flex: 1; color: #94a3b8; gap: 6px;\r\n    font-size: 12px; font-weight: 600;\r\n}\r\n#pth-audio-wrapper .pth-player-ph-icon { font-size: 32px; }\r\n\r\n\/* \u2500\u2500 Tool grid \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-tool-search {\r\n    width: 100%; padding: 9px 14px; border: 2px solid #e2e8f0;\r\n    border-radius: 8px; font-family: 'Inter', sans-serif;\r\n    font-size: 13px; color: #0f172a; margin-bottom: 12px;\r\n    transition: 0.2s;\r\n}\r\n#pth-audio-wrapper .pth-tool-search:focus {\r\n    border-color: #7c3aed; outline: none;\r\n    box-shadow: 0 0 0 3px rgba(124,58,237,0.1);\r\n}\r\n#pth-audio-wrapper .pth-tools-grid {\r\n    display: grid; grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));\r\n    gap: 8px; margin-bottom: 20px;\r\n}\r\n#pth-audio-wrapper .pth-tool-btn {\r\n    background: #fff; border: 2px solid #e2e8f0;\r\n    border-radius: 10px; padding: 12px 6px;\r\n    text-align: center; cursor: pointer; transition: 0.2s;\r\n    display: flex; flex-direction: column;\r\n    align-items: center; justify-content: center;\r\n    gap: 6px; min-height: 72px; font-family: 'Inter', sans-serif;\r\n}\r\n#pth-audio-wrapper .pth-tool-btn:hover {\r\n    border-color: #7c3aed; background: #f5f3ff;\r\n}\r\n#pth-audio-wrapper .pth-tool-btn.pth-active {\r\n    background: #ede9fe; border-color: #7c3aed;\r\n    box-shadow: 0 2px 8px rgba(124,58,237,0.15);\r\n}\r\n#pth-audio-wrapper .pth-tool-icon { font-size: 22px; }\r\n#pth-audio-wrapper .pth-tool-name {\r\n    font-size: 10px; font-weight: 700; color: #1e293b; line-height: 1.2;\r\n}\r\n#pth-audio-wrapper .pth-tool-btn.pth-active .pth-tool-name { color: #6d28d9; }\r\n#pth-audio-wrapper .pth-tool-btn.pth-hidden { display: none; }\r\n\r\n\/* \u2500\u2500 Settings panel \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-settings-panel {\r\n    background: #f8fafc; border: 1px solid #e2e8f0;\r\n    border-radius: 10px; padding: 18px;\r\n    margin-bottom: 20px; min-height: 80px;\r\n}\r\n#pth-audio-wrapper .pth-setting-group { display: none; }\r\n#pth-audio-wrapper .pth-setting-group.pth-active { display: block; }\r\n#pth-audio-wrapper .pth-field { margin-bottom: 14px; }\r\n#pth-audio-wrapper .pth-field:last-child { margin-bottom: 0; }\r\n#pth-audio-wrapper .pth-field label {\r\n    font-size: 11px; font-weight: 800; color: #64748b;\r\n    text-transform: uppercase; letter-spacing: 0.5px;\r\n    margin-bottom: 6px; display: block;\r\n}\r\n#pth-audio-wrapper .pth-field select,\r\n#pth-audio-wrapper .pth-field input[type=\"text\"] {\r\n    width: 100%; padding: 9px 12px;\r\n    border: 2px solid #e2e8f0; border-radius: 8px;\r\n    font-family: 'Inter', sans-serif; font-size: 14px;\r\n    color: #0f172a; background: #fff; transition: 0.2s;\r\n}\r\n#pth-audio-wrapper .pth-field select:focus,\r\n#pth-audio-wrapper .pth-field input[type=\"text\"]:focus {\r\n    border-color: #7c3aed; outline: none;\r\n    box-shadow: 0 0 0 3px rgba(124,58,237,0.1);\r\n}\r\n#pth-audio-wrapper .pth-range-wrap {\r\n    display: flex; align-items: center; gap: 12px;\r\n}\r\n#pth-audio-wrapper .pth-range {\r\n    flex: 1; cursor: pointer; accent-color: #7c3aed; height: 6px;\r\n}\r\n#pth-audio-wrapper .pth-range-val {\r\n    font-weight: 800; color: #5b21b6; font-size: 14px;\r\n    min-width: 48px; text-align: right;\r\n}\r\n#pth-audio-wrapper .pth-trim-grid {\r\n    display: grid; grid-template-columns: 1fr 1fr; gap: 12px;\r\n}\r\n#pth-audio-wrapper .pth-info-text {\r\n    font-size: 13px; color: #64748b; margin: 0; font-weight: 500;\r\n    line-height: 1.6;\r\n}\r\n\r\n\/* \u2500\u2500 Output format selector \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-output-format-bar {\r\n    display: flex; align-items: center; gap: 10px;\r\n    margin-bottom: 16px; flex-wrap: wrap;\r\n}\r\n#pth-audio-wrapper .pth-output-format-bar label {\r\n    font-size: 12px; font-weight: 800; color: #64748b;\r\n    text-transform: uppercase; letter-spacing: 0.4px;\r\n}\r\n#pth-audio-wrapper .pth-fmt-btn {\r\n    padding: 5px 14px; border-radius: 6px; font-size: 12px;\r\n    font-weight: 700; cursor: pointer; border: 2px solid #e2e8f0;\r\n    background: #f8fafc; color: #334155; transition: 0.15s;\r\n    font-family: 'Inter', sans-serif;\r\n}\r\n#pth-audio-wrapper .pth-fmt-btn.pth-fmt-active {\r\n    background: #ede9fe; border-color: #7c3aed; color: #6d28d9;\r\n}\r\n\r\n\/* \u2500\u2500 Process button \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-process-btn {\r\n    width: 100%; padding: 16px; border: none; border-radius: 10px;\r\n    background: linear-gradient(135deg, #7c3aed, #2563eb);\r\n    color: #fff; font-size: 16px; font-weight: 800;\r\n    cursor: pointer; transition: 0.2s; font-family: 'Inter', sans-serif;\r\n    box-shadow: 0 4px 14px rgba(124,58,237,0.3);\r\n    display: flex; align-items: center; justify-content: center; gap: 10px;\r\n}\r\n#pth-audio-wrapper .pth-process-btn:hover {\r\n    transform: translateY(-2px);\r\n    box-shadow: 0 8px 20px rgba(124,58,237,0.35);\r\n}\r\n\r\n\/* \u2500\u2500 Progress bar \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-prog-wrap {\r\n    display: none; background: #e2e8f0; border-radius: 8px;\r\n    overflow: hidden; margin-bottom: 14px;\r\n    position: relative; height: 30px;\r\n}\r\n#pth-audio-wrapper .pth-prog-bar {\r\n    height: 100%; width: 0; background: #7c3aed; transition: width 0.3s;\r\n}\r\n#pth-audio-wrapper .pth-prog-text {\r\n    position: absolute; inset: 0; display: flex;\r\n    align-items: center; justify-content: center;\r\n    font-size: 12px; font-weight: 800; color: #0f172a;\r\n}\r\n\r\n\/* \u2500\u2500 Download button \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-dl-btn {\r\n    display: none; width: 100%; padding: 13px;\r\n    background: #059669; color: #fff; border-radius: 8px;\r\n    font-size: 14px; font-weight: 800; text-align: center;\r\n    text-decoration: none; margin-top: 12px; transition: 0.2s;\r\n    box-shadow: 0 4px 12px rgba(5,150,105,0.25);\r\n}\r\n#pth-audio-wrapper .pth-dl-btn:hover { background: #047857; }\r\n\r\n\/* \u2500\u2500 Processing top banner \u2500\u2500 *\/\r\n#pth-audio-proc-banner {\r\n    position: fixed; top: 0; left: 0; width: 100%;\r\n    background: #f8fafc; border-bottom: 3px solid #7c3aed;\r\n    z-index: 999999; display: none;\r\n    align-items: center; justify-content: center;\r\n    padding: 12px 20px; gap: 14px;\r\n    box-shadow: 0 4px 15px rgba(0,0,0,0.1);\r\n}\r\n#pth-audio-proc-banner .pth-spinner {\r\n    width: 22px; height: 22px; border: 3px solid #cbd5e1;\r\n    border-top-color: #7c3aed; border-radius: 50%;\r\n    animation: pthAudSpin 1s linear infinite; flex-shrink: 0;\r\n}\r\n@keyframes pthAudSpin { to { transform: rotate(360deg); } }\r\n#pth-audio-proc-banner .pth-banner-text {\r\n    font-size: 13px; font-weight: 700; color: #334155;\r\n}\r\n\r\n\/* \u2500\u2500 Processing log \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-log-box {\r\n    background: #0f172a; color: #a78bfa; padding: 14px;\r\n    border-radius: 10px; font-family: 'Courier New', monospace;\r\n    font-size: 11px; height: 130px; overflow-y: auto;\r\n    border: 2px solid #1e293b; margin-top: 16px;\r\n    box-shadow: inset 0 2px 8px rgba(0,0,0,0.4);\r\n}\r\n#pth-audio-wrapper .pth-log-box .pth-log-step {\r\n    color: #fbbf24; font-weight: 700;\r\n}\r\n#pth-audio-wrapper .pth-log-box .pth-log-ok { color: #34d399; }\r\n#pth-audio-wrapper .pth-log-box .pth-log-err { color: #f87171; }\r\n\r\n\/* \u2500\u2500 Toast system \u2500\u2500 *\/\r\n#pth-audio-toast {\r\n    position: fixed; bottom: 24px; right: 24px; z-index: 9999;\r\n    display: flex; flex-direction: column; gap: 10px;\r\n    pointer-events: none;\r\n}\r\n.pth-toast-msg {\r\n    padding: 12px 20px; border-radius: 10px; font-size: 14px;\r\n    font-weight: 600; color: #fff; pointer-events: auto;\r\n    box-shadow: 0 4px 15px rgba(0,0,0,0.15);\r\n    font-family: 'Inter', system-ui, sans-serif;\r\n}\r\n.pth-toast-success { background: #059669; }\r\n.pth-toast-error   { background: #dc2626; }\r\n.pth-toast-info    { background: #2563eb; }\r\n\r\n\/* \u2500\u2500 Sidebar \u2500\u2500 *\/\r\n#pth-audio-wrapper .pth-sidebar-card-header {\r\n    background: #f8fafc; margin: -24px -24px 16px -24px;\r\n    padding: 12px 16px; color: #2563eb; font-size: 12px;\r\n    border-bottom: 2px solid #eff6ff; font-weight: 800;\r\n    text-transform: uppercase; border-radius: 12px 12px 0 0;\r\n    letter-spacing: 0.5px;\r\n}\r\n#pth-audio-wrapper .pth-tip-list {\r\n    list-style: none; padding: 0; margin: 0;\r\n    display: flex; flex-direction: column; gap: 8px;\r\n}\r\n#pth-audio-wrapper .pth-tip-list li {\r\n    font-size: 12px; color: #475569; font-weight: 500;\r\n    line-height: 1.5; padding-left: 16px; position: relative;\r\n}\r\n#pth-audio-wrapper .pth-tip-list li::before {\r\n    content: '\u203a'; position: absolute; left: 0;\r\n    color: #5b21b6; font-weight: 800;\r\n}\r\n#pth-audio-wrapper .pth-stat-row {\r\n    display: flex; justify-content: space-between;\r\n    align-items: center; padding: 8px 0;\r\n    border-bottom: 1px solid #f1f5f9; font-size: 12px;\r\n}\r\n#pth-audio-wrapper .pth-stat-row:last-child { border-bottom: none; }\r\n#pth-audio-wrapper .pth-stat-label { color: #64748b; font-weight: 600; }\r\n#pth-audio-wrapper .pth-stat-val { color: #0f172a; font-weight: 800; }\r\n\r\n\/* \u2500\u2500 Mobile \u2500\u2500 *\/\r\n@media (max-width: 768px) {\r\n    #pth-audio-wrapper {\r\n        padding: 10px; border-radius: 0;\r\n        background: transparent; width: 100% !important;\r\n    }\r\n    #pth-audio-wrapper .pth-card { padding: 14px; margin-bottom: 14px; }\r\n    #pth-audio-wrapper .pth-flex-layout {\r\n        grid-template-columns: 100% !important; gap: 14px;\r\n    }\r\n    #pth-audio-wrapper .pth-tool-title { font-size: 20px; }\r\n    #pth-audio-wrapper .pth-players-grid { grid-template-columns: 1fr; }\r\n    #pth-audio-wrapper .pth-trim-grid { grid-template-columns: 1fr; }\r\n    #pth-audio-wrapper .pth-tools-grid {\r\n        grid-template-columns: repeat(auto-fill, minmax(75px, 1fr));\r\n    }\r\n}\r\n<\/style>\r\n\r\n<!-- Processing top banner \u2014 outside wrapper -->\r\n<div id=\"pth-audio-proc-banner\" role=\"status\" aria-live=\"polite\">\r\n    <div class=\"pth-spinner\"><\/div>\r\n    <div class=\"pth-banner-text\">Processing audio offline\u2026 Browser may ask to wait \u2014 click <strong>Wait<\/strong>. Everything runs locally.<\/div>\r\n<\/div>\r\n\r\n<!-- Toast container -->\r\n<div id=\"pth-audio-toast\" role=\"status\" aria-live=\"polite\"><\/div>\r\n\r\n<div id=\"pth-audio-wrapper\">\r\n    <div class=\"pth-flex-layout\" id=\"pth-audio-layout\">\r\n\r\n        <!-- \u2550\u2550\u2550 MAIN COLUMN \u2550\u2550\u2550 -->\r\n        <div class=\"pth-main-col\">\r\n            <div class=\"pth-card\">\r\n\r\n                <!-- Header -->\r\n                <div style=\"display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:14px;flex-wrap:wrap;gap:10px;\">\r\n                    <h2 class=\"pth-tool-title\">\ud83c\udfb5 Audio Studio Pro<\/h2>\r\n                    <div style=\"display:flex;gap:8px;align-items:center;flex-wrap:wrap;\">\r\n                        <button class=\"pth-btn pth-btn-secondary\" id=\"pth-audio-fw-btn\"\r\n                            aria-label=\"Toggle full width layout\">\u26f6 Full Width<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <!-- Meta bar -->\r\n                <div class=\"pth-tool-meta-bar\">\r\n                    <span class=\"pth-version-badge\">v4.0<\/span>\r\n                    <span class=\"pth-status-offline\">\ud83d\udd12 100% Offline<\/span>\r\n                    <span class=\"pth-engine-badge\" id=\"pth-engine-badge\">\u23f3 Loading Engine\u2026<\/span>\r\n                <\/div>\r\n\r\n                <!-- Engine loading progress bar -->\r\n                <div class=\"pth-engine-load-wrap\" id=\"pth-engine-load-wrap\">\r\n                    <div class=\"pth-engine-load-label\">\r\n                        <span>\u2699\ufe0f Loading FFmpeg Audio Engine\u2026<\/span>\r\n                        <span class=\"pth-engine-load-pct\" id=\"pth-engine-load-pct\">0%<\/span>\r\n                    <\/div>\r\n                    <div class=\"pth-engine-load-bar-bg\">\r\n                        <div class=\"pth-engine-load-bar\" id=\"pth-engine-load-bar\"><\/div>\r\n                    <\/div>\r\n                    <div class=\"pth-engine-load-status\" id=\"pth-engine-load-status\">Fetching FFmpeg WASM core (~30MB one-time download)\u2026<\/div>\r\n                <\/div>\r\n\r\n                <!-- Action bar -->\r\n                <div style=\"display:flex;gap:8px;margin-bottom:18px;flex-wrap:wrap;\">\r\n                    <button class=\"pth-sample-btn\" id=\"pth-audio-sample-btn\"\r\n                        aria-label=\"Load sample audio for testing\">\ud83d\udca1 Load Sample<\/button>\r\n                    <button class=\"pth-btn pth-btn-danger\" id=\"pth-audio-reset-btn\"\r\n                        style=\"font-size:0.75rem;padding:4px 10px;border-radius:6px;\"\r\n                        aria-label=\"Reset studio and clear all files\">\ud83d\udd04 Reset Studio<\/button>\r\n                    <button class=\"pth-btn pth-btn-secondary\" id=\"pth-audio-clear-btn\"\r\n                        style=\"font-size:0.75rem;padding:4px 10px;border-radius:6px;\"\r\n                        aria-label=\"Clear current file\">\ud83d\uddd1\ufe0f Clear<\/button>\r\n                <\/div>\r\n\r\n                <!-- Upload zone -->\r\n                <div class=\"pth-upload-zone\" id=\"pth-upload-zone\">\r\n                    <span class=\"pth-upload-icon\">\ud83d\udcc1<\/span>\r\n                    <label for=\"pth-file-input\" class=\"pth-upload-label pth-disabled\"\r\n                        id=\"pth-upload-label\">\u23f3 Please Wait\u2026<\/label>\r\n                    <input type=\"file\" id=\"pth-file-input\"\r\n                        accept=\"audio\/*,video\/*\" style=\"display:none;\"\r\n                        aria-label=\"Select audio or video file to process\">\r\n                    <div class=\"pth-format-hint\">Supported: MP3, WAV, M4A, OGG, FLAC, MP4 (audio extraction)<\/div>\r\n                    <div class=\"pth-file-info\" id=\"pth-file-info\">\r\n                        \ud83d\udcc4 <strong id=\"pth-filename\"><\/strong> \u2022\r\n                        <span id=\"pth-duration\">\u2014<\/span> \u2022\r\n                        <span id=\"pth-filesize\"><\/span>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <!-- Audio players -->\r\n                <div class=\"pth-players-grid\">\r\n                    <div class=\"pth-player-box\">\r\n                        <div class=\"pth-player-label\">\r\n                            <span>\ud83c\udfa7 Original<\/span>\r\n                            <span class=\"pth-player-size\" id=\"pth-in-size\">\u2014<\/span>\r\n                        <\/div>\r\n                        <div class=\"pth-player-ph\" id=\"pth-in-ph\">\r\n                            <span class=\"pth-player-ph-icon\">\ud83c\udfb5<\/span>\r\n                            <span>Select a file above<\/span>\r\n                        <\/div>\r\n                        <audio id=\"pth-aud-in\" class=\"pth-audio-el\" controls\r\n                            aria-label=\"Original audio player\"><\/audio>\r\n                    <\/div>\r\n                    <div class=\"pth-player-box\">\r\n                        <div class=\"pth-player-label\">\r\n                            <span>\u2728 Output<\/span>\r\n                            <span class=\"pth-player-size out\" id=\"pth-out-size\">\u2014<\/span>\r\n                        <\/div>\r\n                        <div class=\"pth-player-ph\" id=\"pth-out-ph\">\r\n                            <span class=\"pth-player-ph-icon\">\u2699\ufe0f<\/span>\r\n                            <span>Processed audio here<\/span>\r\n                        <\/div>\r\n                        <audio id=\"pth-aud-out\" class=\"pth-audio-el\" controls\r\n                            aria-label=\"Processed audio output player\"><\/audio>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <!-- Tool search -->\r\n                <input type=\"text\" class=\"pth-tool-search\" id=\"pth-tool-search\"\r\n                    placeholder=\"\ud83d\udd0d Search tools\u2026 e.g. echo, trim, pitch\"\r\n                    aria-label=\"Search audio processing tools\">\r\n\r\n                <!-- Tool grid -->\r\n                <div class=\"pth-tools-grid\" id=\"pth-tools-grid\" role=\"group\"\r\n                    aria-label=\"Audio processing tool selector\">\r\n                    <button class=\"pth-tool-btn pth-active\" id=\"pth-btn-convert\"\r\n                        data-tool=\"convert\" data-label=\"To MP3\"\r\n                        aria-label=\"Convert to MP3\" aria-pressed=\"true\">\r\n                        <span class=\"pth-tool-icon\">\ud83d\udd04<\/span>\r\n                        <span class=\"pth-tool-name\">To MP3<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-trim\"\r\n                        data-tool=\"trim\" data-label=\"Trim\"\r\n                        aria-label=\"Trim audio\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\u2702\ufe0f<\/span>\r\n                        <span class=\"pth-tool-name\">Trim<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-volume\"\r\n                        data-tool=\"volume\" data-label=\"Volume\"\r\n                        aria-label=\"Adjust volume\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83d\udd0a<\/span>\r\n                        <span class=\"pth-tool-name\">Volume<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-speed\"\r\n                        data-tool=\"speed\" data-label=\"Speed\"\r\n                        aria-label=\"Change playback speed\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\u26a1<\/span>\r\n                        <span class=\"pth-tool-name\">Speed<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-pitch\"\r\n                        data-tool=\"pitch\" data-label=\"Pitch\"\r\n                        aria-label=\"Shift pitch\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83c\udfb5<\/span>\r\n                        <span class=\"pth-tool-name\">Pitch Shift<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-bass\"\r\n                        data-tool=\"bass\" data-label=\"Bass\"\r\n                        aria-label=\"Boost bass frequencies\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83e\udd41<\/span>\r\n                        <span class=\"pth-tool-name\">Bass Boost<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-normalize\"\r\n                        data-tool=\"normalize\" data-label=\"Normalize\"\r\n                        aria-label=\"Normalize volume levels\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83d\udccf<\/span>\r\n                        <span class=\"pth-tool-name\">Normalize<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-echo\"\r\n                        data-tool=\"echo\" data-label=\"Echo\"\r\n                        aria-label=\"Add echo effect\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83c\udf0a<\/span>\r\n                        <span class=\"pth-tool-name\">Echo<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-reverse\"\r\n                        data-tool=\"reverse\" data-label=\"Reverse\"\r\n                        aria-label=\"Reverse audio\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\u23ea<\/span>\r\n                        <span class=\"pth-tool-name\">Reverse<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-fadein\"\r\n                        data-tool=\"fadein\" data-label=\"Fade In\"\r\n                        aria-label=\"Apply fade in effect\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83d\udd09<\/span>\r\n                        <span class=\"pth-tool-name\">Fade In<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-fadeout\"\r\n                        data-tool=\"fadeout\" data-label=\"Fade Out\"\r\n                        aria-label=\"Apply fade out effect\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83d\udd08<\/span>\r\n                        <span class=\"pth-tool-name\">Fade Out<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-robot\"\r\n                        data-tool=\"robot\" data-label=\"Robot\"\r\n                        aria-label=\"Apply robot voice effect\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83e\udd16<\/span>\r\n                        <span class=\"pth-tool-name\">Robot Voice<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-chipmunk\"\r\n                        data-tool=\"chipmunk\" data-label=\"Chipmunk\"\r\n                        aria-label=\"Apply chipmunk voice effect\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83d\udc3f\ufe0f<\/span>\r\n                        <span class=\"pth-tool-name\">Chipmunk<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-silence\"\r\n                        data-tool=\"silence\" data-label=\"Silence\"\r\n                        aria-label=\"Remove silence from audio\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83e\udd2b<\/span>\r\n                        <span class=\"pth-tool-name\">Cut Silence<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-8d\"\r\n                        data-tool=\"8d\" data-label=\"8D Audio\"\r\n                        aria-label=\"Apply 8D audio effect\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\ud83c\udfa7<\/span>\r\n                        <span class=\"pth-tool-name\">8D Audio<\/span>\r\n                    <\/button>\r\n                    <button class=\"pth-tool-btn\" id=\"pth-btn-tremolo\"\r\n                        data-tool=\"tremolo\" data-label=\"Tremolo\"\r\n                        aria-label=\"Apply tremolo effect\" aria-pressed=\"false\">\r\n                        <span class=\"pth-tool-icon\">\u3030\ufe0f<\/span>\r\n                        <span class=\"pth-tool-name\">Tremolo<\/span>\r\n                    <\/button>\r\n                <\/div>\r\n\r\n                <!-- Settings panel -->\r\n                <div class=\"pth-settings-panel\" id=\"pth-settings-panel\">\r\n\r\n                    <div id=\"pth-set-convert\" class=\"pth-setting-group pth-active\">\r\n                        <div class=\"pth-field\">\r\n                            <label for=\"pth-bit-val\">Audio Quality (Bitrate)<\/label>\r\n                            <select id=\"pth-bit-val\" aria-label=\"Select audio output bitrate\">\r\n                                <option value=\"320k\">320 kbps \u2014 Highest Quality<\/option>\r\n                                <option value=\"192k\" selected>192 kbps \u2014 Standard (Recommended)<\/option>\r\n                                <option value=\"128k\">128 kbps \u2014 Good \/ Smaller File<\/option>\r\n                                <option value=\"64k\">64 kbps \u2014 Small \/ Podcast<\/option>\r\n                            <\/select>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div id=\"pth-set-trim\" class=\"pth-setting-group\">\r\n                        <div class=\"pth-trim-grid\">\r\n                            <div class=\"pth-field\">\r\n                                <label for=\"pth-trim-s\">Start Time (MM:SS)<\/label>\r\n                                <input type=\"text\" id=\"pth-trim-s\" value=\"00:00\"\r\n                                    aria-label=\"Trim start time in MM:SS format\"\r\n                                    placeholder=\"00:00\">\r\n                            <\/div>\r\n                            <div class=\"pth-field\">\r\n                                <label for=\"pth-trim-e\">End Time (MM:SS)<\/label>\r\n                                <input type=\"text\" id=\"pth-trim-e\" value=\"00:00\"\r\n                                    aria-label=\"Trim end time in MM:SS format\"\r\n                                    placeholder=\"00:00\">\r\n                            <\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div id=\"pth-set-volume\" class=\"pth-setting-group\">\r\n                        <div class=\"pth-field\">\r\n                            <label>Volume Level \u2014 <span id=\"pth-vol-val\" style=\"color:#5b21b6;font-weight:800;\">150%<\/span><\/label>\r\n                            <div class=\"pth-range-wrap\">\r\n                                <input type=\"range\" id=\"pth-vol-rng\" class=\"pth-range\"\r\n                                    min=\"0\" max=\"300\" step=\"10\" value=\"150\"\r\n                                    aria-label=\"Volume level percentage\" aria-valuemin=\"0\"\r\n                                    aria-valuemax=\"300\" aria-valuenow=\"150\">\r\n                                <span class=\"pth-range-val\" id=\"pth-vol-disp\">150%<\/span>\r\n                            <\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div id=\"pth-set-speed\" class=\"pth-setting-group\">\r\n                        <div class=\"pth-field\">\r\n                            <label>Playback Speed \u2014 <span id=\"pth-spd-val\" style=\"color:#5b21b6;font-weight:800;\">1.2x<\/span><\/label>\r\n                            <div class=\"pth-range-wrap\">\r\n                                <input type=\"range\" id=\"pth-spd-rng\" class=\"pth-range\"\r\n                                    min=\"0.5\" max=\"2.0\" step=\"0.1\" value=\"1.2\"\r\n                                    aria-label=\"Playback speed multiplier\" aria-valuemin=\"0.5\"\r\n                                    aria-valuemax=\"2.0\" aria-valuenow=\"1.2\">\r\n                                <span class=\"pth-range-val\" id=\"pth-spd-disp\">1.2x<\/span>\r\n                            <\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div id=\"pth-set-pitch\" class=\"pth-setting-group\">\r\n                        <div class=\"pth-field\">\r\n                            <label>Pitch Factor \u2014 <span id=\"pth-pitch-val\" style=\"color:#5b21b6;font-weight:800;\">1.0x<\/span><\/label>\r\n                            <div class=\"pth-range-wrap\">\r\n                                <input type=\"range\" id=\"pth-pitch-rng\" class=\"pth-range\"\r\n                                    min=\"0.5\" max=\"2.0\" step=\"0.1\" value=\"1.0\"\r\n                                    aria-label=\"Pitch shift factor\" aria-valuemin=\"0.5\"\r\n                                    aria-valuemax=\"2.0\" aria-valuenow=\"1.0\">\r\n                                <span class=\"pth-range-val\" id=\"pth-pitch-disp\">1.0x<\/span>\r\n                            <\/div>\r\n                        <\/div>\r\n                    <\/div>\r\n\r\n                    <div id=\"pth-set-bass\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83e\udd41 <strong style=\"color:#0f172a;\">Bass Boost<\/strong> \u2014 Amplifies low frequencies (110Hz) for deeper, punchier bass. Applied with FFmpeg <code>bass=g=10<\/code> filter.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-normalize\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83d\udccf <strong style=\"color:#0f172a;\">Loudness Normalize<\/strong> \u2014 Sets integrated loudness to -16 LUFS (EBU R128). Perfect for podcasts, YouTube uploads, and consistent playback.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-echo\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83c\udf0a <strong style=\"color:#0f172a;\">Echo \/ Reverb<\/strong> \u2014 Adds a natural 1-second echo with 0.3 decay. Uses FFmpeg <code>aecho=0.8:0.9:1000:0.3<\/code>.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-reverse\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\u23ea <strong style=\"color:#0f172a;\">Reverse<\/strong> \u2014 Plays the entire track backwards. Great for creative effects and hidden message detection.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-fadein\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83d\udd09 <strong style=\"color:#0f172a;\">Fade In<\/strong> \u2014 Smooth 3-second volume ramp at the start of the track using FFmpeg <code>afade=t=in:ss=0:d=3<\/code>.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-fadeout\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83d\udd08 <strong style=\"color:#0f172a;\">Fade Out<\/strong> \u2014 Smooth 3-second volume drop at the end of the track. Applied to the last 3 seconds automatically.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-robot\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83e\udd16 <strong style=\"color:#0f172a;\">Robot Voice<\/strong> \u2014 Lowers sample rate to 80% + adds metallic echo. Transforms any voice into a robotic tone.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-chipmunk\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83d\udc3f\ufe0f <strong style=\"color:#0f172a;\">Chipmunk<\/strong> \u2014 Raises sample rate to 150% for a high-pitched, sped-up chipmunk effect. Classic audio fun.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-silence\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83e\udd2b <strong style=\"color:#0f172a;\">Cut Silence<\/strong> \u2014 Detects and removes silent gaps longer than 1 second (threshold: -30dB). Ideal for podcast cleanup.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-8d\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\ud83c\udfa7 <strong style=\"color:#0f172a;\">8D Audio<\/strong> \u2014 Creates an immersive panning effect using <code>apulsator=hz=0.125<\/code>. Requires stereo headphones for full effect.<\/p>\r\n                    <\/div>\r\n                    <div id=\"pth-set-tremolo\" class=\"pth-setting-group\">\r\n                        <p class=\"pth-info-text\">\u3030\ufe0f <strong style=\"color:#0f172a;\">Tremolo<\/strong> \u2014 Applies a 5Hz trembling amplitude modulation for a wavering musical effect.<\/p>\r\n                    <\/div>\r\n\r\n                <\/div>\r\n\r\n                <!-- Output format selector -->\r\n                <div class=\"pth-output-format-bar\">\r\n                    <label>Output Format:<\/label>\r\n                    <button class=\"pth-fmt-btn pth-fmt-active\" id=\"pth-fmt-mp3\"\r\n                        data-fmt=\"mp3\" aria-label=\"Output as MP3\" aria-pressed=\"true\">MP3<\/button>\r\n                    <button class=\"pth-fmt-btn\" id=\"pth-fmt-wav\"\r\n                        data-fmt=\"wav\" aria-label=\"Output as WAV\" aria-pressed=\"false\">WAV<\/button>\r\n                    <button class=\"pth-fmt-btn\" id=\"pth-fmt-ogg\"\r\n                        data-fmt=\"ogg\" aria-label=\"Output as OGG\" aria-pressed=\"false\">OGG<\/button>\r\n                <\/div>\r\n\r\n                <!-- Error box -->\r\n                <div id=\"pth-audio-error\"\r\n                    style=\"display:none;background:#fef2f2;border-left:4px solid #ef4444;color:#b91c1c;padding:14px;margin-bottom:14px;border-radius:8px;font-weight:700;font-size:14px;\"\r\n                    role=\"alert\" aria-live=\"assertive\"><\/div>\r\n\r\n                <!-- Progress -->\r\n                <div class=\"pth-prog-wrap\" id=\"pth-prog-wrap\">\r\n                    <div class=\"pth-prog-bar\" id=\"pth-prog-bar\"><\/div>\r\n                    <div class=\"pth-prog-text\" id=\"pth-prog-text\">0%<\/div>\r\n                <\/div>\r\n\r\n                <!-- Process button -->\r\n                <button class=\"pth-process-btn\" id=\"pth-process-btn\"\r\n                    aria-label=\"Process audio with selected tool\">\r\n                    \ud83d\ude80 PROCESS AUDIO\r\n                <\/button>\r\n\r\n                <!-- Download -->\r\n                <a id=\"pth-dl-btn\" class=\"pth-dl-btn\"\r\n                    aria-label=\"Download processed audio file\">\u2b07\ufe0f Download Output<\/a>\r\n\r\n                <!-- Processing log -->\r\n                <div class=\"pth-log-box\" id=\"pth-log\" aria-live=\"polite\" aria-label=\"Processing log\">\r\n                    <div>> System ready. Select a file to begin.<\/div>\r\n                <\/div>\r\n\r\n            <\/div><!-- \/pth-card -->\r\n        <\/div><!-- \/main col -->\r\n\r\n        <!-- \u2550\u2550\u2550 SIDEBAR \u2550\u2550\u2550 -->\r\n        <div class=\"pth-sidebar-col\" id=\"pth-audio-sidebar\">\r\n\r\n            <!-- File Stats -->\r\n            <div class=\"pth-card\">\r\n                <div class=\"pth-sidebar-card-header\">\ud83d\udcca File Info<\/div>\r\n                <div class=\"pth-stat-row\">\r\n                    <span class=\"pth-stat-label\">File Name<\/span>\r\n                    <span class=\"pth-stat-val\" id=\"pth-stat-name\">\u2014<\/span>\r\n                <\/div>\r\n                <div class=\"pth-stat-row\">\r\n                    <span class=\"pth-stat-label\">Duration<\/span>\r\n                    <span class=\"pth-stat-val\" id=\"pth-stat-dur\">\u2014<\/span>\r\n                <\/div>\r\n                <div class=\"pth-stat-row\">\r\n                    <span class=\"pth-stat-label\">Input Size<\/span>\r\n                    <span class=\"pth-stat-val\" id=\"pth-stat-size\">\u2014<\/span>\r\n                <\/div>\r\n                <div class=\"pth-stat-row\">\r\n                    <span class=\"pth-stat-label\">Active Tool<\/span>\r\n                    <span class=\"pth-stat-val\" id=\"pth-stat-tool\">To MP3<\/span>\r\n                <\/div>\r\n                <div class=\"pth-stat-row\">\r\n                    <span class=\"pth-stat-label\">Output Format<\/span>\r\n                    <span class=\"pth-stat-val\" id=\"pth-stat-fmt\">MP3<\/span>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <!-- Pro Tips -->\r\n            <div class=\"pth-card\">\r\n                <div class=\"pth-sidebar-card-header\">\ud83d\udca1 Pro Tips<\/div>\r\n                <ul class=\"pth-tip-list\">\r\n                    <li>Start with high-bitrate files for best output quality.<\/li>\r\n                    <li>Run <strong>Normalize<\/strong> before export for consistent loudness.<\/li>\r\n                    <li><strong>Cut Silence<\/strong> is perfect for cleaning up podcast recordings.<\/li>\r\n                    <li><strong>8D Audio<\/strong> requires stereo headphones to hear the full effect.<\/li>\r\n                    <li>Be subtle with Pitch \u2014 changes above 1.5x can distort voices.<\/li>\r\n                    <li>Use <strong>Trim<\/strong> first, then apply effects for cleaner results.<\/li>\r\n                <\/ul>\r\n            <\/div>\r\n\r\n            <!-- How to Use -->\r\n            <div class=\"pth-card\">\r\n                <div class=\"pth-sidebar-card-header\">\ud83d\udccb How To Use<\/div>\r\n                <div style=\"display:flex;flex-direction:column;gap:10px;\">\r\n                    <div style=\"display:flex;gap:10px;align-items:flex-start;\">\r\n                        <div style=\"background:#7c3aed;color:#fff;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;flex-shrink:0;\">1<\/div>\r\n                        <span style=\"font-size:12px;color:#475569;font-weight:500;\">Wait for engine \u2705 badge, then upload a file<\/span>\r\n                    <\/div>\r\n                    <div style=\"display:flex;gap:10px;align-items:flex-start;\">\r\n                        <div style=\"background:#7c3aed;color:#fff;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;flex-shrink:0;\">2<\/div>\r\n                        <span style=\"font-size:12px;color:#475569;font-weight:500;\">Select a tool from the grid, adjust settings<\/span>\r\n                    <\/div>\r\n                    <div style=\"display:flex;gap:10px;align-items:flex-start;\">\r\n                        <div style=\"background:#7c3aed;color:#fff;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;flex-shrink:0;\">3<\/div>\r\n                        <span style=\"font-size:12px;color:#475569;font-weight:500;\">Pick Output Format (MP3\/WAV\/OGG)<\/span>\r\n                    <\/div>\r\n                    <div style=\"display:flex;gap:10px;align-items:flex-start;\">\r\n                        <div style=\"background:#7c3aed;color:#fff;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;flex-shrink:0;\">4<\/div>\r\n                        <span style=\"font-size:12px;color:#475569;font-weight:500;\">Click \ud83d\ude80 PROCESS AUDIO \u2192 Download output<\/span>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <!-- Privacy -->\r\n            <div class=\"pth-card\" style=\"background:#fefce8;border-color:#fde68a;\">\r\n                <div class=\"pth-sidebar-card-header\" style=\"color:#d97706;border-color:#fde68a;\">\ud83d\udd12 Privacy<\/div>\r\n                <p style=\"font-size:12px;color:#78350f;line-height:1.7;margin:0;font-weight:500;\">\r\n                    <strong>Zero data leaves your device.<\/strong> FFmpeg WASM runs entirely inside your browser tab. No uploads, no server, no tracking. Works offline after the first engine load.\r\n                <\/p>\r\n            <\/div>\r\n\r\n        <\/div><!-- \/sidebar -->\r\n    <\/div><!-- \/flex-layout -->\r\n<\/div><!-- \/wrapper -->\r\n\r\n<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n    'use strict';\r\n\r\n    \/* =======================================================\r\n       PTH AUDIO STUDIO PRO \u2014 v4.0\r\n       WP\/LiteSpeed safe | WCAG 2.1 AA | PageSpeed 100\r\n       FFmpeg WASM via separate header Woody snippet\r\n       ======================================================= *\/\r\n\r\n    window.pthAudioStudioApp = {\r\n        initialized: false,\r\n\r\n        \/* \u2500\u2500\u2500 State \u2500\u2500\u2500 *\/\r\n        file: null,\r\n        ffmpeg: null,\r\n        isProcessing: false,\r\n        currentTool: 'convert',\r\n        currentFormat: 'mp3',\r\n        engineReady: false,\r\n\r\n        \/* \u2500\u2500\u2500 INIT \u2500\u2500\u2500 *\/\r\n        init: function () {\r\n            this.attachEvents();\r\n            this.initEngine();\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 EVENTS \u2014 addEventListener ONLY \u2500\u2500\u2500 *\/\r\n        attachEvents: function () {\r\n            var self = this;\r\n\r\n            \/* Full Width *\/\r\n            var fwBtn = document.getElementById('pth-audio-fw-btn');\r\n            if (fwBtn) fwBtn.addEventListener('click', function () { self.toggleWidth(); });\r\n\r\n            \/* File input *\/\r\n            var fileInput = document.getElementById('pth-file-input');\r\n            if (fileInput) fileInput.addEventListener('change', function () { self.handleFile(this); });\r\n\r\n            \/* Tool buttons *\/\r\n            var toolBtns = document.querySelectorAll('#pth-tools-grid .pth-tool-btn');\r\n            toolBtns.forEach(function (btn) {\r\n                btn.addEventListener('click', function () {\r\n                    self.setTool(this.getAttribute('data-tool'), this.getAttribute('data-label'));\r\n                });\r\n            });\r\n\r\n            \/* Format buttons *\/\r\n            var fmtBtns = document.querySelectorAll('.pth-fmt-btn');\r\n            fmtBtns.forEach(function (btn) {\r\n                btn.addEventListener('click', function () { self.setFormat(this.getAttribute('data-fmt')); });\r\n            });\r\n\r\n            \/* Range sliders *\/\r\n            var volRng = document.getElementById('pth-vol-rng');\r\n            if (volRng) volRng.addEventListener('input', function () {\r\n                document.getElementById('pth-vol-val').textContent = this.value + '%';\r\n                document.getElementById('pth-vol-disp').textContent = this.value + '%';\r\n                this.setAttribute('aria-valuenow', this.value);\r\n            });\r\n\r\n            var spdRng = document.getElementById('pth-spd-rng');\r\n            if (spdRng) spdRng.addEventListener('input', function () {\r\n                var v = parseFloat(this.value).toFixed(1) + 'x';\r\n                document.getElementById('pth-spd-val').textContent = v;\r\n                document.getElementById('pth-spd-disp').textContent = v;\r\n                this.setAttribute('aria-valuenow', this.value);\r\n            });\r\n\r\n            var pitchRng = document.getElementById('pth-pitch-rng');\r\n            if (pitchRng) pitchRng.addEventListener('input', function () {\r\n                var v = parseFloat(this.value).toFixed(1) + 'x';\r\n                document.getElementById('pth-pitch-val').textContent = v;\r\n                document.getElementById('pth-pitch-disp').textContent = v;\r\n                this.setAttribute('aria-valuenow', this.value);\r\n            });\r\n\r\n            \/* Process button *\/\r\n            var processBtn = document.getElementById('pth-process-btn');\r\n            if (processBtn) processBtn.addEventListener('click', function () { self.process(); });\r\n\r\n            \/* Sample button *\/\r\n            var sampleBtn = document.getElementById('pth-audio-sample-btn');\r\n            if (sampleBtn) sampleBtn.addEventListener('click', function () { self.loadSample(); });\r\n\r\n            \/* Reset button *\/\r\n            var resetBtn = document.getElementById('pth-audio-reset-btn');\r\n            if (resetBtn) resetBtn.addEventListener('click', function () { self.resetStudio(); });\r\n\r\n            \/* Clear button *\/\r\n            var clearBtn = document.getElementById('pth-audio-clear-btn');\r\n            if (clearBtn) clearBtn.addEventListener('click', function () { self.clearFile(); });\r\n\r\n            \/* Tool search *\/\r\n            var searchInput = document.getElementById('pth-tool-search');\r\n            if (searchInput) searchInput.addEventListener('input', function () { self.filterTools(this.value); });\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 ENGINE INIT \u2500\u2500\u2500 *\/\r\n        initEngine: function () {\r\n            var self = this;\r\n\r\n            \/* \u2500\u2500 Update engine loading progress bar \u2500\u2500 *\/\r\n            self.setEngineProgress = function (pct, status) {\r\n                var wrap = document.getElementById('pth-engine-load-wrap');\r\n                var bar  = document.getElementById('pth-engine-load-bar');\r\n                var pctEl = document.getElementById('pth-engine-load-pct');\r\n                var statEl = document.getElementById('pth-engine-load-status');\r\n                if (wrap) wrap.classList.add('pth-visible');\r\n                if (bar) bar.style.width = pct + '%';\r\n                if (pctEl) pctEl.textContent = pct + '%';\r\n                if (statEl && status) statEl.textContent = status;\r\n            };\r\n            self.hideEngineProgress = function () {\r\n                var wrap = document.getElementById('pth-engine-load-wrap');\r\n                if (wrap) wrap.classList.remove('pth-visible');\r\n            };\r\n\r\n            \/* \u2500\u2500 Dynamically load FFmpeg if not present (no header snippet needed) \u2500\u2500 *\/\r\n            function loadFFmpegScript(callback) {\r\n                if (typeof FFmpeg !== 'undefined') { callback(); return; }\r\n                self.setEngineProgress(5, 'Loading FFmpeg library script...');\r\n                var script = document.createElement('script');\r\n                script.src = 'https:\/\/unpkg.com\/@ffmpeg\/ffmpeg@0.11.6\/dist\/ffmpeg.min.js';\r\n                script.setAttribute('data-no-optimize', '1');\r\n                script.setAttribute('data-no-minify', '1');\r\n                script.setAttribute('data-cfasync', 'false');\r\n                script.onload = function () {\r\n                    self.setEngineProgress(20, 'FFmpeg library ready. Fetching WASM core...');\r\n                    callback();\r\n                };\r\n                script.onerror = function () {\r\n                    self.setEngineProgress(0, 'Engine load failed \u2014 check internet connection.');\r\n                    self.log('Failed to load FFmpeg script from CDN.', 'err');\r\n                    var badge = document.getElementById('pth-engine-badge');\r\n                    if (badge) {\r\n                        badge.textContent = 'Load Failed';\r\n                        badge.style.background = '#fef2f2';\r\n                        badge.style.color = '#991b1b';\r\n                        badge.style.borderColor = '#fecaca';\r\n                    }\r\n                };\r\n                document.head.appendChild(script);\r\n            }\r\n\r\n            \/* \u2500\u2500 Staged progress simulation for visual feedback during ~30MB WASM download \u2500\u2500 *\/\r\n            function runLoadingStages() {\r\n                var stages = [\r\n                    { pct: 30, msg: 'Fetching WASM core (~30MB, one-time download)...', delay: 500 },\r\n                    { pct: 50, msg: 'Compiling WebAssembly modules...', delay: 1500 },\r\n                    { pct: 68, msg: 'Initializing audio codec libraries...', delay: 1000 },\r\n                    { pct: 82, msg: 'Preparing single-thread safe engine...', delay: 800 }\r\n                ];\r\n                var i = 0;\r\n                function next() {\r\n                    if (i >= stages.length) return;\r\n                    var s = stages[i++];\r\n                    setTimeout(function () {\r\n                        if (!self.engineReady) self.setEngineProgress(s.pct, s.msg);\r\n                        next();\r\n                    }, s.delay);\r\n                }\r\n                next();\r\n            }\r\n\r\n            setTimeout(function () {\r\n                self.setEngineProgress(2, 'Starting audio engine...');\r\n                loadFFmpegScript(function () {\r\n                    try {\r\n                        var createFFmpeg = FFmpeg.createFFmpeg;\r\n                        self.log('Initializing Safe Single-Thread Engine v4.0...');\r\n                        self.setEngineProgress(22, 'Creating FFmpeg instance...');\r\n\r\n                        self.ffmpeg = createFFmpeg({\r\n                            mainName: 'main',\r\n                            corePath: 'https:\/\/unpkg.com\/@ffmpeg\/core-st@0.11.1\/dist\/ffmpeg-core.js',\r\n                            log: false,\r\n                            logger: function (p) {\r\n                                if (p.message && p.message.includes('size=')) self.log(p.message);\r\n                                if (p.message && p.message.includes('ffmpeg-core.wasm')) {\r\n                                    if (!self.engineReady) self.setEngineProgress(65, 'Downloading WASM binary...');\r\n                                }\r\n                            },\r\n                            progress: function (p) {\r\n                                \/* During AUDIO processing only (after engine ready) *\/\r\n                                if (self.engineReady) {\r\n                                    var pct = Math.max(0, Math.min(100, Math.round(p.ratio * 100)));\r\n                                    var bar = document.getElementById('pth-prog-bar');\r\n                                    var txt = document.getElementById('pth-prog-text');\r\n                                    if (bar) bar.style.width = pct + '%';\r\n                                    if (txt) txt.textContent = 'Processing: ' + pct + '%';\r\n                                }\r\n                            }\r\n                        });\r\n\r\n                        runLoadingStages();\r\n\r\n                        self.ffmpeg.load().then(function () {\r\n                            self.engineReady = true;\r\n                            self.setEngineProgress(100, 'Engine loaded and ready!');\r\n                            setTimeout(function () { self.hideEngineProgress(); }, 1500);\r\n\r\n                            var badge = document.getElementById('pth-engine-badge');\r\n                            if (badge) { badge.textContent = 'Engine Ready'; badge.classList.add('ready'); }\r\n                            var lbl = document.getElementById('pth-upload-label');\r\n                            if (lbl) { lbl.classList.remove('pth-disabled'); lbl.textContent = 'Select Audio \/ Video'; }\r\n                            self.log('Engine loaded successfully - v4.0 ready.', 'ok');\r\n                            self.showToast('Audio Engine Ready!', 'success');\r\n\r\n                        }).catch(function (e) {\r\n                            self.setEngineProgress(0, 'Engine load failed: ' + e.message);\r\n                            self.log('Engine load error: ' + e.message, 'err');\r\n                            var badge = document.getElementById('pth-engine-badge');\r\n                            if (badge) {\r\n                                badge.textContent = 'Load Failed';\r\n                                badge.style.background = '#fef2f2';\r\n                                badge.style.color = '#991b1b';\r\n                                badge.style.borderColor = '#fecaca';\r\n                            }\r\n                        });\r\n\r\n                    } catch (e) {\r\n                        self.setEngineProgress(0, 'Init error: ' + e.message);\r\n                        self.log('Engine init error: ' + e.message, 'err');\r\n                    }\r\n                });\r\n            }, 300);\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 FILE HANDLER \u2500\u2500\u2500 *\/\r\n        handleFile: function (input) {\r\n            if (!input.files || input.files.length === 0) return;\r\n            var self = this;\r\n            self.file = input.files[0];\r\n\r\n            var sizeMB = (self.file.size \/ 1048576).toFixed(2) + ' MB';\r\n            var name = self.file.name.length > 22\r\n                ? self.file.name.substring(0, 19) + '\u2026'\r\n                : self.file.name;\r\n\r\n            \/* Update file info *\/\r\n            var info = document.getElementById('pth-file-info');\r\n            if (info) info.style.display = 'block';\r\n            var fnEl = document.getElementById('pth-filename');\r\n            if (fnEl) fnEl.textContent = self.file.name;\r\n            var fsEl = document.getElementById('pth-filesize');\r\n            if (fsEl) fsEl.textContent = sizeMB;\r\n\r\n            \/* Update stats sidebar *\/\r\n            var statName = document.getElementById('pth-stat-name');\r\n            if (statName) statName.textContent = name;\r\n            var statSize = document.getElementById('pth-stat-size');\r\n            if (statSize) statSize.textContent = sizeMB;\r\n            var inSize = document.getElementById('pth-in-size');\r\n            if (inSize) inSize.textContent = sizeMB;\r\n\r\n            \/* Upload zone styling *\/\r\n            var zone = document.getElementById('pth-upload-zone');\r\n            if (zone) zone.classList.add('has-file');\r\n\r\n            \/* Load into input player *\/\r\n            var url = URL.createObjectURL(self.file);\r\n            var audIn = document.getElementById('pth-aud-in');\r\n            var inPh = document.getElementById('pth-in-ph');\r\n            if (audIn) {\r\n                audIn.src = url;\r\n                audIn.style.display = 'block';\r\n                audIn.load();\r\n                if (inPh) inPh.style.display = 'none';\r\n            }\r\n\r\n            \/* Detect duration *\/\r\n            if (audIn) {\r\n                audIn.addEventListener('loadedmetadata', function onMeta() {\r\n                    audIn.removeEventListener('loadedmetadata', onMeta);\r\n                    var dur = audIn.duration;\r\n                    if (isFinite(dur) && dur > 0) {\r\n                        var min = String(Math.floor(dur \/ 60)).padStart(2, '0');\r\n                        var sec = String(Math.floor(dur % 60)).padStart(2, '0');\r\n                        var durStr = min + ':' + sec;\r\n                        var durEl = document.getElementById('pth-duration');\r\n                        if (durEl) durEl.textContent = durStr;\r\n                        var trimE = document.getElementById('pth-trim-e');\r\n                        if (trimE) trimE.value = durStr;\r\n                        var statDur = document.getElementById('pth-stat-dur');\r\n                        if (statDur) statDur.textContent = durStr;\r\n                        self.log('File loaded: ' + self.file.name + ' | Duration: ' + durStr);\r\n                    }\r\n                });\r\n                audIn.addEventListener('canplay', function onPlay() {\r\n                    audIn.removeEventListener('canplay', onPlay);\r\n                    var dur = audIn.duration;\r\n                    if (isFinite(dur) && dur > 0) {\r\n                        var min = String(Math.floor(dur \/ 60)).padStart(2, '0');\r\n                        var sec = String(Math.floor(dur % 60)).padStart(2, '0');\r\n                        var trimE = document.getElementById('pth-trim-e');\r\n                        if (trimE && trimE.value === '00:00') trimE.value = min + ':' + sec;\r\n                    }\r\n                });\r\n            }\r\n\r\n            \/* Reset output area *\/\r\n            var dlBtn = document.getElementById('pth-dl-btn');\r\n            if (dlBtn) dlBtn.style.display = 'none';\r\n            var audOut = document.getElementById('pth-aud-out');\r\n            var outPh = document.getElementById('pth-out-ph');\r\n            if (audOut) audOut.style.display = 'none';\r\n            if (outPh) outPh.style.display = 'flex';\r\n\r\n            self.showToast('File loaded: ' + self.file.name, 'info');\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 SET TOOL \u2500\u2500\u2500 *\/\r\n        setTool: function (toolId, label) {\r\n            var self = this;\r\n            self.currentTool = toolId;\r\n\r\n            \/* Update button states *\/\r\n            document.querySelectorAll('#pth-tools-grid .pth-tool-btn').forEach(function (btn) {\r\n                btn.classList.remove('pth-active');\r\n                btn.setAttribute('aria-pressed', 'false');\r\n            });\r\n            var activeBtn = document.getElementById('pth-btn-' + toolId);\r\n            if (activeBtn) {\r\n                activeBtn.classList.add('pth-active');\r\n                activeBtn.setAttribute('aria-pressed', 'true');\r\n            }\r\n\r\n            \/* Show matching settings group *\/\r\n            document.querySelectorAll('.pth-setting-group').forEach(function (g) {\r\n                g.classList.remove('pth-active');\r\n            });\r\n            var settingGroup = document.getElementById('pth-set-' + toolId);\r\n            if (settingGroup) settingGroup.classList.add('pth-active');\r\n\r\n            \/* Update sidebar stat *\/\r\n            var statTool = document.getElementById('pth-stat-tool');\r\n            if (statTool) statTool.textContent = label || toolId;\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 SET OUTPUT FORMAT \u2500\u2500\u2500 *\/\r\n        setFormat: function (fmt) {\r\n            this.currentFormat = fmt;\r\n            document.querySelectorAll('.pth-fmt-btn').forEach(function (btn) {\r\n                btn.classList.remove('pth-fmt-active');\r\n                btn.setAttribute('aria-pressed', 'false');\r\n            });\r\n            var activeBtn = document.getElementById('pth-fmt-' + fmt);\r\n            if (activeBtn) {\r\n                activeBtn.classList.add('pth-fmt-active');\r\n                activeBtn.setAttribute('aria-pressed', 'true');\r\n            }\r\n            var statFmt = document.getElementById('pth-stat-fmt');\r\n            if (statFmt) statFmt.textContent = fmt.toUpperCase();\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 TOOL SEARCH \/ FILTER \u2500\u2500\u2500 *\/\r\n        filterTools: function (query) {\r\n            var q = query.toLowerCase().trim();\r\n            document.querySelectorAll('#pth-tools-grid .pth-tool-btn').forEach(function (btn) {\r\n                var label = (btn.getAttribute('data-label') || '').toLowerCase();\r\n                var tool = (btn.getAttribute('data-tool') || '').toLowerCase();\r\n                if (!q || label.indexOf(q) > -1 || tool.indexOf(q) > -1) {\r\n                    btn.classList.remove('pth-hidden');\r\n                } else {\r\n                    btn.classList.add('pth-hidden');\r\n                }\r\n            });\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 PROCESS \u2500\u2500\u2500 *\/\r\n        process: async function () {\r\n            var self = this;\r\n            if (self.isProcessing) { self.showToast('Already processing. Please wait.', 'info'); return; }\r\n            if (!self.file) { self.showToast('Please select an audio file first.', 'error'); return; }\r\n            if (!self.engineReady || !self.ffmpeg) { self.showToast('Engine not ready yet. Please wait.', 'error'); return; }\r\n\r\n            self.isProcessing = true;\r\n\r\n            \/* Show banner + progress *\/\r\n            var banner = document.getElementById('pth-audio-proc-banner');\r\n            var processBtn = document.getElementById('pth-process-btn');\r\n            var progWrap = document.getElementById('pth-prog-wrap');\r\n            var progBar = document.getElementById('pth-prog-bar');\r\n            var progTxt = document.getElementById('pth-prog-text');\r\n            var dlBtn = document.getElementById('pth-dl-btn');\r\n            var audOut = document.getElementById('pth-aud-out');\r\n            var outPh = document.getElementById('pth-out-ph');\r\n            var errBox = document.getElementById('pth-audio-error');\r\n\r\n            if (banner) banner.style.display = 'flex';\r\n            if (processBtn) { processBtn.disabled = true; processBtn.textContent = '\u23f3 Processing\u2026'; }\r\n            if (progWrap) progWrap.style.display = 'block';\r\n            if (progBar) progBar.style.width = '0%';\r\n            if (progTxt) progTxt.textContent = 'Starting\u2026';\r\n            if (dlBtn) dlBtn.style.display = 'none';\r\n            if (audOut) audOut.style.display = 'none';\r\n            if (outPh) outPh.style.display = 'flex';\r\n            if (errBox) errBox.style.display = 'none';\r\n\r\n            await new Promise(function (r) { setTimeout(r, 300); });\r\n\r\n            \/* Unique session IDs \u2014 prevent FFmpeg FS overwrite conflicts *\/\r\n            var runId = Date.now();\r\n            var ext = self.file.name.split('.').pop().toLowerCase();\r\n            var fIn = 'in_' + runId + '.' + ext;\r\n            var fmt = self.currentFormat;\r\n            var mimeMap = { mp3: 'audio\/mpeg', wav: 'audio\/wav', ogg: 'audio\/ogg' };\r\n            var fOut = 'out_' + runId + '.' + fmt;\r\n            var o = self.currentTool;\r\n\r\n            try {\r\n                self.log('[1\/5] Initializing audio sandbox\u2026', 'step');\r\n\r\n                var fileData = await new Promise(function (res, rej) {\r\n                    var rd = new FileReader();\r\n                    rd.onload = function () { res(new Uint8Array(rd.result)); };\r\n                    rd.onerror = function () { rej(new Error('FileReader failed')); };\r\n                    rd.readAsArrayBuffer(self.file);\r\n                });\r\n\r\n                self.log('[2\/5] Loading media into WASM memory\u2026', 'step');\r\n                self.ffmpeg.FS('writeFile', fIn, fileData);\r\n\r\n                self.log('[3\/5] Building FFmpeg command for ' + o.toUpperCase() + '\u2026', 'step');\r\n\r\n                var args = ['-i', fIn, '-vn'];\r\n\r\n                if (o === 'convert') {\r\n                    args.push('-b:a', document.getElementById('pth-bit-val').value);\r\n                } else if (o === 'trim') {\r\n                    var ss = self.parseTime(document.getElementById('pth-trim-s').value);\r\n                    var ee = self.parseTime(document.getElementById('pth-trim-e').value);\r\n                    var dur = ee - ss;\r\n                    args = ['-ss', String(ss), '-i', fIn, '-vn'];\r\n                    if (dur > 0) args.push('-t', String(dur));\r\n                    args.push('-b:a', '192k');\r\n                } else if (o === 'volume') {\r\n                    var vol = document.getElementById('pth-vol-rng').value \/ 100;\r\n                    args.push('-filter:a', 'volume=' + vol, '-b:a', '192k');\r\n                } else if (o === 'speed') {\r\n                    var spd = document.getElementById('pth-spd-rng').value;\r\n                    args.push('-filter:a', 'atempo=' + spd, '-b:a', '192k');\r\n                } else if (o === 'pitch') {\r\n                    var factor = parseFloat(document.getElementById('pth-pitch-rng').value);\r\n                    args.push('-filter:a', 'asetrate=44100*' + factor + ',aresample=44100', '-b:a', '192k');\r\n                } else if (o === 'bass') {\r\n                    args.push('-af', 'bass=g=10:f=110:w=0.6', '-b:a', '192k');\r\n                } else if (o === 'normalize') {\r\n                    args.push('-filter:a', 'loudnorm=I=-16:TP=-1.5:LRA=11', '-b:a', '192k');\r\n                } else if (o === 'echo') {\r\n                    args.push('-filter:a', 'aecho=0.8:0.9:1000:0.3', '-b:a', '192k');\r\n                } else if (o === 'reverse') {\r\n                    args.push('-filter:a', 'areverse', '-b:a', '192k');\r\n                } else if (o === 'fadein') {\r\n                    args.push('-filter:a', 'afade=t=in:ss=0:d=3', '-b:a', '192k');\r\n                } else if (o === 'fadeout') {\r\n                    args.push('-filter:a', 'afade=t=out:st=last_duration-3:d=3', '-b:a', '192k');\r\n                } else if (o === 'robot') {\r\n                    args.push('-filter:a', 'asetrate=44100*0.8,aresample=44100,aecho=0.8:0.88:60:0.4', '-b:a', '192k');\r\n                } else if (o === 'chipmunk') {\r\n                    args.push('-filter:a', 'asetrate=44100*1.5,aresample=44100', '-b:a', '192k');\r\n                } else if (o === 'silence') {\r\n                    args.push('-filter:a', 'silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-30dB', '-b:a', '192k');\r\n                } else if (o === '8d') {\r\n                    args.push('-filter:a', 'apulsator=hz=0.125', '-b:a', '192k');\r\n                } else if (o === 'tremolo') {\r\n                    args.push('-filter:a', 'tremolo=f=5.0:d=0.5', '-b:a', '192k');\r\n                }\r\n\r\n                \/* WAV\/OGG format overrides *\/\r\n                if (fmt === 'wav') {\r\n                    args.push('-acodec', 'pcm_s16le');\r\n                } else if (fmt === 'ogg') {\r\n                    args.push('-c:a', 'libvorbis', '-q:a', '4');\r\n                }\r\n\r\n                args.push(fOut);\r\n\r\n                self.log('[4\/5] Executing FFmpeg render\u2026 (browser may pause briefly)', 'step');\r\n\r\n                try {\r\n                    await self.ffmpeg.run.apply(self.ffmpeg, args);\r\n                } catch (runErr) {\r\n                    \/* FFmpeg Emscripten exit(0) false alarm \u2014 safe to ignore *\/\r\n                    if (runErr.message && runErr.message.indexOf('exit(0)') > -1) {\r\n                        self.log('Engine signaled completion (exit 0). Proceeding\u2026');\r\n                    } else {\r\n                        throw runErr;\r\n                    }\r\n                }\r\n\r\n                self.log('[5\/5] Packaging output file\u2026', 'step');\r\n\r\n                var outData = self.ffmpeg.FS('readFile', fOut);\r\n                var outMime = mimeMap[fmt] || 'audio\/mpeg';\r\n                var outBlob = new Blob([outData.buffer], { type: outMime });\r\n                var outUrl = URL.createObjectURL(outBlob);\r\n                var outSizeMB = (outBlob.size \/ 1048576).toFixed(2) + ' MB';\r\n\r\n                \/* Update output player *\/\r\n                if (audOut) {\r\n                    audOut.src = outUrl;\r\n                    audOut.style.display = 'block';\r\n                    audOut.load();\r\n                    if (outPh) outPh.style.display = 'none';\r\n                }\r\n\r\n                \/* Update download button *\/\r\n                if (dlBtn) {\r\n                    dlBtn.href = outUrl;\r\n                    dlBtn.download = 'pth-audio-' + o + '-' + runId + '.' + fmt;\r\n                    dlBtn.style.display = 'block';\r\n                    dlBtn.textContent = '\\u2b07\\ufe0f Download ' + fmt.toUpperCase() + ' \u2014 ' + outSizeMB;\r\n                }\r\n\r\n                var outSizeEl = document.getElementById('pth-out-size');\r\n                if (outSizeEl) outSizeEl.textContent = outSizeMB;\r\n\r\n                if (progBar) progBar.style.width = '100%';\r\n                if (progTxt) progTxt.textContent = 'Complete! 100%';\r\n\r\n                self.log('Task complete: ' + o.toUpperCase() + ' \u2192 ' + fmt.toUpperCase() + ' (' + outSizeMB + ')', 'ok');\r\n                self.showToast('Done! ' + fmt.toUpperCase() + ' ready to download.', 'success');\r\n\r\n                \/* Cleanup WASM virtual memory *\/\r\n                try { self.ffmpeg.FS('unlink', fIn); } catch (e) { \/* ignore *\/ }\r\n                try { self.ffmpeg.FS('unlink', fOut); } catch (e) { \/* ignore *\/ }\r\n\r\n            } catch (err) {\r\n                console.error('[PTH Audio]', err);\r\n                if (progBar) progBar.style.background = '#ef4444';\r\n                if (progTxt) progTxt.textContent = 'Failed. Try Reset Studio.';\r\n                if (errBox) {\r\n                    errBox.textContent = 'Processing failed: ' + (err.message || 'Unknown error. Try Reset Studio.');\r\n                    errBox.style.display = 'block';\r\n                }\r\n                self.log('Error: ' + (err.message || 'Unknown'), 'err');\r\n                self.showToast('Processing failed. Try Reset Studio.', 'error');\r\n            }\r\n\r\n            if (banner) banner.style.display = 'none';\r\n            if (processBtn) { processBtn.disabled = false; processBtn.textContent = '\\ud83d\\ude80 PROCESS AUDIO'; }\r\n            self.isProcessing = false;\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 LOAD SAMPLE \u2500\u2500\u2500 *\/\r\n        loadSample: function () {\r\n            var self = this;\r\n            if (!self.engineReady) {\r\n                self.showToast('Engine still loading. Please wait.', 'info');\r\n                return;\r\n            }\r\n            \/* Generate a simple 2-second sine wave tone as sample using Web Audio API *\/\r\n            try {\r\n                var ctx = new (window.AudioContext || window.webkitAudioContext)();\r\n                var osc = ctx.createOscillator();\r\n                var dest = ctx.createMediaStreamDestination();\r\n                osc.connect(dest);\r\n                osc.frequency.value = 440;\r\n                osc.type = 'sine';\r\n                osc.start();\r\n\r\n                var rec = new MediaRecorder(dest.stream);\r\n                var chunks = [];\r\n                rec.ondataavailable = function (e) { chunks.push(e.data); };\r\n                rec.onstop = function () {\r\n                    var blob = new Blob(chunks, { type: 'audio\/webm' });\r\n                    var fakeFile = new File([blob], 'sample-440hz-tone.webm', { type: 'audio\/webm' });\r\n                    self.file = fakeFile;\r\n\r\n                    var url = URL.createObjectURL(blob);\r\n                    var audIn = document.getElementById('pth-aud-in');\r\n                    var inPh = document.getElementById('pth-in-ph');\r\n                    if (audIn) {\r\n                        audIn.src = url;\r\n                        audIn.style.display = 'block';\r\n                        if (inPh) inPh.style.display = 'none';\r\n                        audIn.load();\r\n                    }\r\n\r\n                    var zone = document.getElementById('pth-upload-zone');\r\n                    if (zone) zone.classList.add('has-file');\r\n\r\n                    var fnEl = document.getElementById('pth-filename');\r\n                    if (fnEl) fnEl.textContent = 'sample-440hz-tone.webm';\r\n                    var info = document.getElementById('pth-file-info');\r\n                    if (info) info.style.display = 'block';\r\n                    var statName = document.getElementById('pth-stat-name');\r\n                    if (statName) statName.textContent = 'sample-440hz-tone.webm';\r\n                    var statDur = document.getElementById('pth-stat-dur');\r\n                    if (statDur) statDur.textContent = '00:02';\r\n\r\n                    ctx.close();\r\n                    self.log('Sample tone loaded: 440Hz sine wave, 2 seconds');\r\n                    self.showToast('Sample audio loaded! Now choose a tool and process.', 'success');\r\n                };\r\n\r\n                rec.start();\r\n                setTimeout(function () { osc.stop(); rec.stop(); }, 2000);\r\n\r\n            } catch (e) {\r\n                self.showToast('Sample generation not supported in this browser. Please upload a file.', 'info');\r\n            }\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 RESET STUDIO \u2500\u2500\u2500 *\/\r\n        resetStudio: function () {\r\n            var self = this;\r\n            self.file = null;\r\n            self.isProcessing = false;\r\n\r\n            \/* Reset file input *\/\r\n            var fileInput = document.getElementById('pth-file-input');\r\n            if (fileInput) fileInput.value = '';\r\n\r\n            \/* Reset upload zone *\/\r\n            var zone = document.getElementById('pth-upload-zone');\r\n            if (zone) zone.classList.remove('has-file');\r\n            var info = document.getElementById('pth-file-info');\r\n            if (info) info.style.display = 'none';\r\n\r\n            \/* Reset players *\/\r\n            var audIn = document.getElementById('pth-aud-in');\r\n            var audOut = document.getElementById('pth-aud-out');\r\n            var inPh = document.getElementById('pth-in-ph');\r\n            var outPh = document.getElementById('pth-out-ph');\r\n            if (audIn) { audIn.src = ''; audIn.style.display = 'none'; }\r\n            if (audOut) { audOut.src = ''; audOut.style.display = 'none'; }\r\n            if (inPh) inPh.style.display = 'flex';\r\n            if (outPh) outPh.style.display = 'flex';\r\n\r\n            \/* Reset sizes *\/\r\n            var inSize = document.getElementById('pth-in-size');\r\n            var outSize = document.getElementById('pth-out-size');\r\n            if (inSize) inSize.textContent = '\u2014';\r\n            if (outSize) outSize.textContent = '\u2014';\r\n\r\n            \/* Reset stats *\/\r\n            ['pth-stat-name', 'pth-stat-dur', 'pth-stat-size'].forEach(function (id) {\r\n                var el = document.getElementById(id);\r\n                if (el) el.textContent = '\u2014';\r\n            });\r\n\r\n            \/* Reset trim fields *\/\r\n            var trimS = document.getElementById('pth-trim-s');\r\n            var trimE = document.getElementById('pth-trim-e');\r\n            if (trimS) trimS.value = '00:00';\r\n            if (trimE) trimE.value = '00:00';\r\n\r\n            \/* Reset progress *\/\r\n            var progWrap = document.getElementById('pth-prog-wrap');\r\n            var progBar = document.getElementById('pth-prog-bar');\r\n            var progTxt = document.getElementById('pth-prog-text');\r\n            if (progWrap) progWrap.style.display = 'none';\r\n            if (progBar) { progBar.style.width = '0%'; progBar.style.background = '#7c3aed'; }\r\n            if (progTxt) progTxt.textContent = '0%';\r\n\r\n            \/* Reset download *\/\r\n            var dlBtn = document.getElementById('pth-dl-btn');\r\n            if (dlBtn) dlBtn.style.display = 'none';\r\n\r\n            \/* Reset error *\/\r\n            var errBox = document.getElementById('pth-audio-error');\r\n            if (errBox) errBox.style.display = 'none';\r\n\r\n            \/* Clear log *\/\r\n            var logBox = document.getElementById('pth-log');\r\n            if (logBox) logBox.innerHTML = '<div>> Studio reset. Ready for next file.<\/div>';\r\n\r\n            self.showToast('Studio reset!', 'info');\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 CLEAR FILE (keep tool selection) \u2500\u2500\u2500 *\/\r\n        clearFile: function () {\r\n            this.resetStudio();\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 FULL WIDTH TOGGLE \u2500\u2500\u2500 *\/\r\n        toggleWidth: function () {\r\n            var layout = document.getElementById('pth-audio-layout');\r\n            var sidebar = document.getElementById('pth-audio-sidebar');\r\n            var btn = document.getElementById('pth-audio-fw-btn');\r\n            if (!layout || !sidebar || !btn) return;\r\n            if (layout.classList.contains('pth-full-width')) {\r\n                layout.classList.remove('pth-full-width');\r\n                sidebar.style.display = '';\r\n                btn.textContent = '\\u29c6 Full Width';\r\n            } else {\r\n                layout.classList.add('pth-full-width');\r\n                sidebar.style.display = 'none';\r\n                btn.textContent = '\\u25e8 Split View';\r\n            }\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 PARSE TIME MM:SS \u2192 seconds \u2500\u2500\u2500 *\/\r\n        parseTime: function (t) {\r\n            if (!t) return 0;\r\n            if (t.indexOf(':') > -1) {\r\n                var p = t.split(':');\r\n                return parseInt(p[0] || 0, 10) * 60 + parseInt(p[1] || 0, 10);\r\n            }\r\n            return parseFloat(t) || 0;\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 PROCESSING LOG \u2500\u2500\u2500 *\/\r\n        log: function (msg, type) {\r\n            var logBox = document.getElementById('pth-log');\r\n            if (!logBox) return;\r\n            var time = new Date().toLocaleTimeString();\r\n            var div = document.createElement('div');\r\n            if (type === 'step') {\r\n                div.innerHTML = '<span class=\"pth-log-step\">' + msg + '<\/span>';\r\n            } else if (type === 'ok') {\r\n                div.innerHTML = '<span class=\"pth-log-ok\">\\u2713 ' + msg + '<\/span>';\r\n            } else if (type === 'err') {\r\n                div.innerHTML = '<span class=\"pth-log-err\">\\u2717 ' + msg + '<\/span>';\r\n            } else {\r\n                div.textContent = '> [' + time + '] ' + msg;\r\n            }\r\n            logBox.appendChild(div);\r\n            logBox.scrollTop = logBox.scrollHeight;\r\n        },\r\n\r\n        \/* \u2500\u2500\u2500 TOAST SYSTEM \u2014 bottom-right, no alert() \u2500\u2500\u2500 *\/\r\n        showToast: function (msg, type) {\r\n            var container = document.getElementById('pth-audio-toast');\r\n            if (!container) return;\r\n            var toast = document.createElement('div');\r\n            toast.className = 'pth-toast-msg pth-toast-' + (type || 'info');\r\n            toast.textContent = msg;\r\n            container.appendChild(toast);\r\n            setTimeout(function () {\r\n                if (toast.parentNode) toast.parentNode.removeChild(toast);\r\n            }, 3500);\r\n        }\r\n    };\r\n\r\n    \/* =======================================================\r\n       BOOTSTRAP \u2014 WP\/LiteSpeed 4-layer safe init (v3.0)\r\n       ======================================================= *\/\r\n    function runInit() {\r\n        if (!window.pthAudioStudioApp.initialized &&\r\n            document.getElementById('pth-audio-wrapper')) {\r\n            window.pthAudioStudioApp.initialized = true;\r\n            window.pthAudioStudioApp.init();\r\n        }\r\n    }\r\n\r\n    \/* Layer 1: Immediate *\/\r\n    runInit();\r\n\r\n    \/* Layer 2: DOMContentLoaded *\/\r\n    if (document.readyState === 'loading') {\r\n        document.addEventListener('DOMContentLoaded', runInit);\r\n    }\r\n\r\n    \/* Layer 3: window.load + 50ms *\/\r\n    window.addEventListener('load', function () { setTimeout(runInit, 50); });\r\n\r\n    \/* Layer 4: MutationObserver \u2014 async Woody injection *\/\r\n    if (!window.pthAudioStudioApp.initialized && typeof MutationObserver !== 'undefined') {\r\n        var pthObs = new MutationObserver(function () {\r\n            if (document.getElementById('pth-audio-wrapper')) {\r\n                pthObs.disconnect();\r\n                runInit();\r\n            }\r\n        });\r\n        pthObs.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n        setTimeout(function () { pthObs.disconnect(); }, 10000);\r\n    }\r\n\r\n})();\r\n<\/script>\r\n\t\t\t\t\t\t\t\t<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n  'use strict';\r\n  function mount() {\r\n    var panel = document.getElementById('pth-as-tab-audio');\r\n    var tool  = document.getElementById('pth-audio-wrapper');\r\n    if (!panel || !tool) return false;\r\n    if (tool.parentNode !== panel) {\r\n      var ph = document.getElementById('pth-as-empty-audio');\r\n      if (ph && ph.parentNode) ph.parentNode.removeChild(ph);\r\n      tool.style.margin = '0';\r\n      panel.appendChild(tool);\r\n    }\r\n    return true;\r\n  }\r\n  if (mount()) return;\r\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);\r\n  window.addEventListener('load', function () { setTimeout(mount, 60); });\r\n  if (typeof MutationObserver !== 'undefined') {\r\n    var o = new MutationObserver(function () { if (mount()) o.disconnect(); });\r\n    o.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n    setTimeout(function () { o.disconnect(); }, 10000);\r\n  }\r\n})();\r\n<\/script>\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n     HEADER WOODY SNIPPET (separate snippet \u2014 location: header)\r\n     Add this as a SEPARATE Woody snippet with location: header\r\n     <script src=\"https:\/\/unpkg.com\/@ffmpeg\/ffmpeg@0.11.6\/dist\/ffmpeg.min.js\"\r\n       data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\"><\/script>\r\n     \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n\n\n<script src=\"https:\/\/unpkg.com\/wavesurfer.js@7\"><\/script>\r\n<script src=\"https:\/\/unpkg.com\/wavesurfer.js@7\/dist\/plugins\/regions.min.js\"><\/script>\r\n<script src=\"https:\/\/unpkg.com\/wavesurfer.js@7\/dist\/plugins\/timeline.min.js\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/lamejs\/1.2.1\/lame.min.js\"><\/script>\r\n\r\n<style>\r\n\/* ==========================================================================\r\n   PRIME TOOL HUB - ALL-IN-ONE PROFESSIONAL AUDIO STUDIO (Tool 54 - v6.4)\r\n   Engine: Web Audio API | Pure CSS Icons | Joiner Trimmer | Mic Rec Restored\r\n   ========================================================================== *\/\r\n#pth-audio-studio-wrapper { font-family: 'Inter', system-ui, sans-serif; max-width: 1550px; margin: 0 auto; box-sizing: border-box; background: #f8fafc; padding: 24px; border-radius: 20px; color: #0f172a; position: relative; box-shadow: 0 10px 30px -5px rgba(0,0,0,0.08); }\r\n#pth-audio-studio-wrapper * { box-sizing: border-box; min-width: 0; }\r\n\r\n\/* Top Bar *\/\r\n#pth-audio-studio-wrapper .pth-top-bar { background: #ffffff; border: 1px solid #e2e8f0; padding: 20px 25px; border-radius: 12px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); flex-wrap: wrap; gap: 15px;}\r\n#pth-audio-studio-wrapper .pth-tool-title { font-size: 1.7rem; font-weight: 800; color: #1e293b; margin: 0; display: flex; align-items: center; gap: 12px;}\r\n#pth-audio-studio-wrapper .pth-subtitle { font-size: 0.95rem; color: #64748b; font-weight: 600; margin-top: 4px; display: block;}\r\n#pth-audio-studio-wrapper .pth-flex-layout { display: grid; grid-template-columns: 1.8fr 1fr; gap: 25px; align-items: start; transition: 0.3s ease; }\r\n@media (max-width: 1024px) { #pth-audio-studio-wrapper .pth-flex-layout { grid-template-columns: 1fr; } }\r\n#pth-audio-studio-wrapper .pth-flex-layout.pth-full-width { grid-template-columns: 1fr; }\r\n\r\n\/* Multi-Tool Tabs *\/\r\n#pth-audio-studio-wrapper .pth-tabs-nav { display: flex; gap: 8px; background: #ffffff; padding: 10px; border-radius: 12px; border: 1px solid #e2e8f0; margin-bottom: 25px; overflow-x: auto; white-space: nowrap; box-shadow: 0 2px 4px rgba(0,0,0,0.02);}\r\n#pth-audio-studio-wrapper .pth-tab-btn { padding: 12px 20px; font-weight: 800; font-size: 0.95rem; color: #64748b; background: transparent; border: none; border-radius: 8px; cursor: pointer; transition: 0.3s; flex: 1; text-align: center; }\r\n#pth-audio-studio-wrapper .pth-tab-btn:hover { background: #f1f5f9; color: #3b82f6; }\r\n#pth-audio-studio-wrapper .pth-tab-btn.active { background: #eff6ff; color: #1d4ed8; box-shadow: inset 0 0 0 2px #3b82f6; }\r\n#pth-audio-studio-wrapper .pth-tab-pane { display: none; animation: pthFadeIn 0.4s ease; }\r\n#pth-audio-studio-wrapper .pth-tab-pane.active { display: block; }\r\n@keyframes pthFadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }\r\n\r\n\/* Cards & Upload Area *\/\r\n#pth-audio-studio-wrapper .pth-card { background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 25px; margin-bottom: 25px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.02); }\r\n#pth-audio-studio-wrapper .pth-card-title { font-size: 1.2rem; font-weight: 800; color: #1e293b; margin: 0 0 15px 0; display: flex; align-items: center; gap: 8px; padding-bottom: 15px; border-bottom: 2px solid #e2e8f0;}\r\n#pth-audio-studio-wrapper .pth-drop-zone { border: 2px dashed #3b82f6; border-radius: 12px; padding: 40px 20px; text-align: center; background: linear-gradient(135deg, #eff6ff, #dbeafe); transition: all 0.3s; cursor: pointer; margin-bottom: 20px;}\r\n#pth-audio-studio-wrapper .pth-drop-zone:hover { border-color: #1e40af; background: #dbeafe; transform: translateY(-2px); }\r\n#pth-audio-studio-wrapper .pth-dz-icon { font-size: 48px; margin-bottom: 12px; }\r\n#pth-audio-studio-wrapper .pth-dz-text { font-size: 1.15rem; font-weight: 800; color: #1e3a8a; margin: 0 0 8px 0; }\r\n#pth-audio-studio-wrapper .pth-dz-sub { font-size: 0.9rem; color: #64748b; margin: 0; font-weight: 600;}\r\n\r\n\/* Form Inputs *\/\r\n#pth-audio-studio-wrapper .pth-label { font-size: 0.85rem; font-weight: 700; color: #475569; margin-bottom: 8px; display: block; text-transform: uppercase; letter-spacing: 0.5px;}\r\n#pth-audio-studio-wrapper .pth-input, #pth-audio-studio-wrapper .pth-select { width: 100%; padding: 12px 16px; border: 2px solid #cbd5e1; border-radius: 8px; font-size: 0.95rem; font-weight: 600; outline:none; transition: 0.3s; font-family: 'Inter', sans-serif;}\r\n#pth-audio-studio-wrapper .pth-input:focus, #pth-audio-studio-wrapper .pth-select:focus { border-color: #3b82f6; box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.15); }\r\n\r\n\/* Buttons *\/\r\n#pth-audio-studio-wrapper .pth-btn { padding: 12px 24px; border-radius: 8px; font-weight: 800; font-size: 0.95rem; cursor: pointer; transition: all 0.2s; display: inline-flex; align-items: center; justify-content: center; gap: 8px; border: none; font-family: 'Inter', sans-serif;}\r\n#pth-audio-studio-wrapper .pth-btn-primary { background: linear-gradient(135deg, #3b82f6, #1d4ed8); color: white; box-shadow: 0 4px 12px rgba(59,130,246,0.3); }\r\n#pth-audio-studio-wrapper .pth-btn-primary:hover { background: linear-gradient(135deg, #2563eb, #1e40af); transform: translateY(-2px); box-shadow: 0 6px 15px rgba(59,130,246,0.4); }\r\n#pth-audio-studio-wrapper .pth-btn-success { background: linear-gradient(135deg, #10b981, #047857); color: white; box-shadow: 0 4px 12px rgba(16,185,129,0.3); width: 100%; font-size: 1.1rem; padding: 16px;}\r\n#pth-audio-studio-wrapper .pth-btn-success:hover { background: linear-gradient(135deg, #059669, #065f46); transform: translateY(-2px); box-shadow: 0 6px 15px rgba(16,185,129,0.4); }\r\n#pth-audio-studio-wrapper .pth-btn-danger { background: #ef4444; color: white; box-shadow: 0 4px 12px rgba(239,68,68,0.3); }\r\n#pth-audio-studio-wrapper .pth-btn-danger:hover { background: #dc2626; transform: translateY(-2px); }\r\n#pth-audio-studio-wrapper .pth-btn-secondary { background: #e2e8f0; color: #475569; }\r\n#pth-audio-studio-wrapper .pth-btn-secondary:hover { background: #cbd5e1; color: #0f172a;}\r\n#pth-audio-studio-wrapper .pth-btn-small { padding: 8px 14px; font-size: 0.85rem; border-radius: 6px; }\r\n\r\n\/* Premium Dark Player Workspace *\/\r\n#pth-audio-studio-wrapper .pth-workspace { display: none; }\r\n#pth-audio-studio-wrapper .pth-result-workspace { display: none; border-top: 4px solid #f59e0b; }\r\n#pth-audio-studio-wrapper .pth-dark-player-box { background: #0f172a; padding: 15px; border-radius: 12px; margin-bottom: 20px; border: 3px solid #1e293b; box-shadow: inset 0 4px 20px rgba(0,0,0,0.6); display: flex; flex-direction: column;}\r\n#pth-audio-studio-wrapper .pth-dark-player-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding: 0 5px;}\r\n#pth-audio-studio-wrapper .pth-dark-file-name { color: #f8fafc; font-weight: 700; font-size: 0.95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60%;}\r\n#pth-audio-studio-wrapper .pth-dark-time { color: #3b82f6; font-family: monospace; font-size: 1.1rem; font-weight: 800; background: rgba(0,0,0,0.5); padding: 4px 10px; border-radius: 6px;}\r\n\r\n#pth-audio-studio-wrapper .pth-waveform-container { width: 100%; height: 110px; position: relative; }\r\n#pth-audio-studio-wrapper .pth-timeline-container { width: 100%; height: 25px; margin-top: 5px; }\r\n#pth-audio-studio-wrapper .pth-video-preview { width: 100%; max-height: 250px; background: #000; border-radius: 8px; margin-bottom: 15px; display: none; border: 1px solid #334155;}\r\n\r\n\/* Playback Controls (Pure CSS Shapes) *\/\r\n#pth-audio-studio-wrapper .pth-playback-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin-bottom: 20px; padding: 15px; background: #f8fafc; border-radius: 12px; border: 1px solid #e2e8f0;}\r\n#pth-audio-studio-wrapper .pth-play-btn { width: 64px; height: 64px; border-radius: 50%; background: linear-gradient(135deg, #3b82f6, #1e40af); border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: 0.2s; box-shadow: 0 8px 20px rgba(59,130,246,0.4); flex-shrink: 0;}\r\n#pth-audio-studio-wrapper .pth-play-btn.result { background: linear-gradient(135deg, #f59e0b, #d97706); box-shadow: 0 8px 20px rgba(245,158,11,0.4); }\r\n#pth-audio-studio-wrapper .pth-play-btn:hover { transform: scale(1.08); box-shadow: 0 10px 25px rgba(59,130,246,0.5); }\r\n#pth-audio-studio-wrapper .pth-ctrl-btn { width: 46px; height: 46px; border-radius: 50%; background: #ffffff; border: 1px solid #cbd5e1; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: 0.2s; box-shadow: 0 2px 5px rgba(0,0,0,0.05); flex-shrink: 0;}\r\n#pth-audio-studio-wrapper .pth-ctrl-btn:hover { background: #f1f5f9; border-color: #94a3b8; transform: translateY(-2px); }\r\n\r\n\/* Pure CSS Icons (Never Stripped by WordPress) *\/\r\n.pth-css-play { width: 0; height: 0; border-style: solid; border-width: 10px 0 10px 16px; border-color: transparent transparent transparent #ffffff; margin-left: 4px; }\r\n.pth-css-pause { width: 6px; height: 18px; background: #ffffff; box-shadow: 10px 0 0 #ffffff; margin-right: 10px; display: none; }\r\n.pth-css-stop { width: 14px; height: 14px; background: #475569; }\r\n.pth-css-backward { width: 0; height: 0; border-style: solid; border-width: 7px 10px 7px 0; border-color: transparent #475569 transparent transparent; position: relative; margin-left: 10px;}\r\n.pth-css-backward::after { content: ''; position: absolute; width: 0; height: 0; border-style: solid; border-width: 7px 10px 7px 0; border-color: transparent #475569 transparent transparent; left: -10px; top: -7px; }\r\n.pth-css-forward { width: 0; height: 0; border-style: solid; border-width: 7px 0 7px 10px; border-color: transparent transparent transparent #475569; position: relative; margin-right: 10px;}\r\n.pth-css-forward::after { content: ''; position: absolute; width: 0; height: 0; border-style: solid; border-width: 7px 0 7px 10px; border-color: transparent transparent transparent #475569; left: 0px; top: -7px; }\r\n\r\n\/* DSP Grid & EQ *\/\r\n#pth-audio-studio-wrapper .pth-tools-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 15px; margin-bottom: 25px; }\r\n#pth-audio-studio-wrapper .pth-tool-box { background: #fff; border: 1px solid #e2e8f0; padding: 18px 14px; border-radius: 10px; text-align: center; transition: 0.3s; }\r\n#pth-audio-studio-wrapper .pth-tool-box:hover { border-color: #3b82f6; transform: translateY(-3px); box-shadow: 0 10px 15px -3px rgba(59,130,246,0.15); }\r\n#pth-audio-studio-wrapper .pth-tool-icon { font-size: 2.2rem; margin-bottom: 12px; }\r\n#pth-audio-studio-wrapper .pth-tool-title { font-size: 0.95rem; font-weight: 800; color: #1e293b; margin-bottom: 6px;}\r\n\r\n#pth-audio-studio-wrapper .pth-toggle-wrap { display: flex; align-items: center; justify-content: center; gap: 10px; font-size: 0.85rem; font-weight: 700; color: #475569; margin-top: 10px;}\r\n#pth-audio-studio-wrapper .pth-switch { position: relative; display: inline-block; width: 44px; height: 24px; }\r\n#pth-audio-studio-wrapper .pth-switch input { opacity: 0; width: 0; height: 0; }\r\n#pth-audio-studio-wrapper .pth-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #cbd5e1; transition: .4s; border-radius: 24px; }\r\n#pth-audio-studio-wrapper .pth-slider:before { position: absolute; content: \"\"; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }\r\n#pth-audio-studio-wrapper input:checked + .pth-slider { background-color: #10b981; }\r\n#pth-audio-studio-wrapper input:checked + .pth-slider:before { transform: translateX(20px); }\r\n\r\n\/* Vertical EQ Sliders *\/\r\n#pth-audio-studio-wrapper .pth-eq-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 25px; padding: 15px 0; }\r\n#pth-audio-studio-wrapper .pth-eq-slider-wrap { display: flex; flex-direction: column; align-items: center; gap: 12px; }\r\n#pth-audio-studio-wrapper .pth-eq-label { font-weight: 800; font-size: 0.9rem; color: #1e293b; }\r\n#pth-audio-studio-wrapper .pth-range-vertical { -webkit-appearance: slider-vertical; width: 8px; height: 130px; accent-color: #3b82f6; outline: none; cursor: pointer; }\r\n#pth-audio-studio-wrapper .pth-range-input { width: 100%; accent-color: #3b82f6; cursor: pointer; }\r\n#pth-audio-studio-wrapper .pth-range-label { display: flex; justify-content: space-between; font-size: 0.85rem; font-weight: 700; color: #64748b; margin-bottom: 8px; }\r\n\r\n\/* Joiner UI *\/\r\n#pth-audio-studio-wrapper .pth-joiner-list { list-style: none; padding: 0; margin: 0 0 20px 0; border: 2px solid #e2e8f0; border-radius: 12px; background: #f8fafc;}\r\n#pth-audio-studio-wrapper .pth-joiner-item { display: flex; justify-content: space-between; align-items: center; padding: 14px 18px; border-bottom: 1px solid #e2e8f0; font-size: 0.95rem; color: #1e293b; font-weight: 700; background:#fff;}\r\n#pth-audio-studio-wrapper .pth-joiner-item:last-child { border-bottom: none; }\r\n#pth-audio-studio-wrapper .pth-joiner-actions-btn { display: flex; gap: 8px; }\r\n#pth-audio-studio-wrapper .pth-joiner-edit { background: #dbeafe; color: #1e40af; border: none; padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 0.8rem; font-weight: 700; transition: 0.2s; }\r\n#pth-audio-studio-wrapper .pth-joiner-edit:hover { background: #bfdbfe; }\r\n#pth-audio-studio-wrapper .pth-joiner-play { background: #d1fae5; color: #047857; border: none; padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 0.8rem; font-weight: 700; transition: 0.2s; }\r\n#pth-audio-studio-wrapper .pth-joiner-play:hover { background: #a7f3d0; }\r\n#pth-audio-studio-wrapper .pth-joiner-del { color: #ef4444; background: #fee2e2; border: none; cursor: pointer; font-size: 1.1rem; width: 30px; height: 30px; border-radius: 6px; display: flex; align-items: center; justify-content: center; transition: 0.2s; font-family: monospace; font-weight:bold;}\r\n#pth-audio-studio-wrapper .pth-joiner-del:hover { background: #fca5a5; color: #991b1b; }\r\n\r\n\/* Overlay & Misc *\/\r\n#pth-audio-studio-wrapper .pth-sidebar-col { position: sticky; top: 20px; height: fit-content; }\r\n#pth-audio-studio-wrapper .pth-toast { position: fixed; bottom: 24px; right: 24px; background: #1e293b; color: white; padding: 14px 26px; border-radius: 8px; font-weight: 700; font-size: 0.95rem; box-shadow: 0 10px 30px rgba(0,0,0,0.3); z-index: 9999; display: none; }\r\n#pth-audio-studio-wrapper .pth-toast.show { display: flex; align-items:center; gap:10px; animation: pthSlideIn 0.3s cubic-bezier(0.4,0,0.2,1); }\r\n@keyframes pthSlideIn { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }\r\n\r\n#pth-audio-studio-wrapper .pth-processing-overlay { position: absolute; top:0; left:0; width:100%; height:100%; background:rgba(255,255,255,0.96); z-index:50; display:none; flex-direction:column; justify-content:center; align-items:center; border-radius:12px; padding: 20px;}\r\n#pth-audio-studio-wrapper .pth-spinner { width: 48px; height: 48px; border: 5px solid rgba(59,130,246,0.2); border-radius: 50%; border-top-color: #3b82f6; animation: pthSpin 1s linear infinite; margin-bottom: 18px;}\r\n@keyframes pthSpin { to { transform: rotate(360deg); } }\r\n@keyframes pthPulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }\r\n\r\n#pth-audio-studio-wrapper .pth-render-bar-wrap { width: 85%; max-width: 450px; height: 14px; background: #e2e8f0; border-radius: 9999px; overflow: hidden; margin-top: 15px; box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);}\r\n#pth-audio-studio-wrapper .pth-render-bar { height: 100%; background: linear-gradient(90deg, #3b82f6, #8b5cf6); width: 0%; transition: width 0.2s ease;}\r\n\r\n\/* Full Page Initial Loader *\/\r\n#pth-audio-studio-wrapper .pth-initial-load { display:none; align-items:center; justify-content:center; gap:15px; padding:20px; background:#eff6ff; border:2px dashed #93c5fd; border-radius:12px; margin-bottom:20px; color:#1e3a8a; font-weight:700;}\r\n<\/style>\r\n\r\n<div id=\"pth-audio-studio-wrapper\">\r\n    <div id=\"pth-aud-toast\" class=\"pth-toast\">\u2705 Action Successful!<\/div>\r\n    \r\n    <div class=\"pth-top-bar\">\r\n        <div>\r\n            <h2 class=\"pth-tool-title\">\ud83c\udf99\ufe0f Ultimate Audio Studio Multi-Tool<\/h2>\r\n            <span class=\"pth-subtitle\">AI Noise Remover \u2022 Audio Joiner \u2022 MP3 Converter & Splitter (100% Offline)<\/span>\r\n            <div style=\"display:flex; gap:8px; margin-top:8px; flex-wrap:wrap;\">\r\n                <span style=\"background:#eff6ff; color:#1d4ed8; font-size:.72rem; font-weight:700; padding:3px 10px; border-radius:20px; border:1px solid #bfdbfe;\">v2.0<\/span>\r\n                <span style=\"background:#f0fdf4; color:#047857; font-size:.72rem; font-weight:700; padding:3px 10px; border-radius:20px; border:1px solid #bbf7d0;\">\ud83d\udd12 100% Offline<\/span>\r\n            <\/div>\r\n        <\/div>\r\n        <div style=\"display:flex; align-items:center; gap:12px; flex-wrap:wrap;\">\r\n            <button class=\"pth-btn pth-btn-secondary pth-btn-small\" id=\"pth-fw-btn-audio\" aria-label=\"Toggle full width layout\">\u26f6 Full Width<\/button>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <div class=\"pth-tabs-nav\">\r\n        <button class=\"pth-tab-btn active\" data-target=\"tab-studio\">\ud83c\udf9b\ufe0f AI Studio & Trimmer<\/button>\r\n        <button class=\"pth-tab-btn\" data-target=\"tab-joiner\">\ud83d\udd17 Audio Joiner<\/button>\r\n        <button class=\"pth-tab-btn\" data-target=\"tab-converter\">\ud83d\udd04 Audio Converter<\/button>\r\n    <\/div>\r\n\r\n    <div class=\"pth-initial-load\" id=\"pth-initial-load\">\r\n        <div class=\"pth-spinner\" style=\"width:24px; height:24px; margin:0; border-width:3px;\"><\/div>\r\n        Loading & Decoding Audio... This may take a few seconds for large files.\r\n    <\/div>\r\n\r\n    <div class=\"pth-flex-layout\" id=\"pth-audio-studio-layout\">\r\n        <div class=\"pth-main-col\">\r\n            \r\n            <div class=\"pth-tab-pane active\" id=\"tab-studio\">\r\n                \r\n                <div class=\"pth-card\" id=\"pth-upload-area\" style=\"border-top: 4px solid #3b82f6;\">\r\n                    <h2 class=\"pth-card-title\">\ud83d\udce4 Upload or Record to Edit<\/h2>\r\n                    <div class=\"pth-drop-zone\" id=\"pth-audio-dz\">\r\n                        <div class=\"pth-dz-icon\">\ud83c\udfa7<\/div>\r\n                        <h3 class=\"pth-dz-text\">Click \/ Drag Audio or Video File<\/h3>\r\n                        <p class=\"pth-dz-sub\">MP4 \u2022 MP3 \u2022 WAV \u2022 OGG \u2022 M4A<\/p>\r\n                    <\/div>\r\n                    <input type=\"file\" id=\"pth-audio-file\" accept=\"audio\/*, video\/*\" style=\"display:none;\">\r\n                    <div style=\"display:flex; justify-content:center; margin-top:15px;\">\r\n                        <button id=\"pth-record-btn\" class=\"pth-btn pth-btn-primary\" style=\"width: 100%;\">\ud83c\udfa4 Start Mic Recording<\/button>\r\n\t\t\t  <button id=\"pth-stop-record-btn\" class=\"pth-btn pth-btn-danger\" style=\"display:none; width: 100%;\">\u23f9\ufe0f Stop & Load Recording<\/button>\r\n                    <\/div>\r\n                    <div id=\"pth-recording-status\" style=\"display:none; text-align:center; margin-top:15px; font-weight:800; color:#ef4444; font-size:1.1rem; animation: pthPulse 1.5s infinite;\">\r\n                        \ud83d\udd34 Recording \u2022 <span id=\"pth-rec-time\">00:00<\/span>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div id=\"pth-workspace\" style=\"display:none;\">\r\n                    <div class=\"pth-card\" style=\"border-top: 4px solid #10b981; position: relative;\">\r\n                        <div class=\"pth-processing-overlay\" id=\"pth-render-overlay\">\r\n                            <div class=\"pth-spinner\"><\/div>\r\n                            <h3 style=\"margin:0; color:#1e293b;\">Rendering Master Track...<\/h3>\r\n                            <p style=\"font-size:0.95rem; color:#64748b; margin-top:8px;\">Encoding high-quality audio offline.<\/p>\r\n                            <div class=\"pth-render-bar-wrap\"><div class=\"pth-render-bar\" id=\"pth-render-bar\"><\/div><\/div>\r\n                        <\/div>\r\n\r\n                        <div style=\"display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;\">\r\n                            <h2 class=\"pth-card-title\" style=\"margin:0; border:none; padding:0;\">\ud83c\udf9b\ufe0f Audio Editor<\/h2>\r\n                            <button class=\"pth-btn pth-btn-secondary pth-btn-small\" id=\"pth-close-workspace\">\u2716 Close File<\/button>\r\n                        <\/div>\r\n\r\n                        <div class=\"pth-dark-player-box\">\r\n                            <div class=\"pth-dark-player-header\">\r\n                                <span class=\"pth-dark-file-name\" id=\"pth-file-name\"><\/span>\r\n                                <span class=\"pth-dark-time\" id=\"pth-time-display\">00:00.0<\/span>\r\n                            <\/div>\r\n                            \r\n                            <video id=\"pth-video-preview\" class=\"pth-video-preview\" controls><\/video>\r\n\r\n                            <div class=\"pth-waveform-container\" id=\"pth-waveform\" style=\"border:none; margin-bottom:0; box-shadow:none; height:100px;\"><\/div>\r\n                            <div class=\"pth-timeline-container\" id=\"pth-timeline\"><\/div>\r\n                        <\/div>\r\n\r\n                        <div class=\"pth-playback-controls\">\r\n                            <button class=\"pth-ctrl-btn\" id=\"pth-btn-backward\" title=\"Skip Back\" aria-label=\"Skip backward\"><div class=\"pth-css-backward\"><\/div><\/button>\r\n                            <button class=\"pth-play-btn\" id=\"pth-btn-play\" title=\"Play \/ Pause\" aria-label=\"Play or pause\">\r\n                                <div class=\"pth-css-play\" id=\"pth-icon-play\"><\/div>\r\n                                <div class=\"pth-css-pause\" id=\"pth-icon-pause\"><\/div>\r\n                            <\/button>\r\n                            <button class=\"pth-ctrl-btn\" id=\"pth-btn-stop\" title=\"Stop\" aria-label=\"Stop playback\"><div class=\"pth-css-stop\"><\/div><\/button>\r\n                            <button class=\"pth-ctrl-btn\" id=\"pth-btn-forward\" title=\"Skip Forward\" aria-label=\"Skip forward\"><div class=\"pth-css-forward\"><\/div><\/button>\r\n                        <\/div>\r\n                        \r\n                        <div style=\"margin-bottom:25px;\">\r\n                            <button id=\"pth-ws-record-btn\" class=\"pth-btn pth-btn-danger\" style=\"width:100%; font-size:1.05rem; padding:14px; background:linear-gradient(135deg, #ef4444, #b91c1c);\">\ud83c\udfa4 Start New Mic Recording Here<\/button>\r\n                            <button id=\"pth-ws-stop-record-btn\" class=\"pth-btn pth-btn-secondary\" style=\"display:none; width:100%; font-size:1.05rem; padding:14px; border:2px solid #ef4444; color:#ef4444;\">\u23f9\ufe0f Stop & Load Recording<\/button>\r\n                            <div id=\"pth-ws-recording-status\" style=\"display:none; text-align:center; margin-top:10px; font-weight:800; color:#ef4444; font-size:1.1rem; animation: pthPulse 1.5s infinite;\">\r\n                                \ud83d\udd34 Recording \u2022 <span id=\"pth-ws-rec-time\">00:00<\/span>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div style=\"background:#fffbeb; border:2px solid #fde68a; padding:18px; border-radius:12px; margin-bottom:25px; display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:15px;\">\r\n                            <div>\r\n                                <strong style=\"color:#b45309; font-size:1.05rem;\">\u2702\ufe0f Audio Splitter \/ Cutter:<\/strong>\r\n                                <span style=\"color:#92400e; font-size:0.9rem; display:block; margin-top:4px;\">Drag to highlight a region on the waveform first.<\/span>\r\n                            <\/div>\r\n                            <div style=\"display:flex; gap:8px; flex-wrap:wrap;\">\r\n                                <button class=\"pth-btn pth-btn-danger pth-btn-small\" id=\"pth-cut-mid-btn\" title=\"Deletes the highlighted middle part\" aria-label=\"Delete selected region\">\u2702\ufe0f Delete Selected<\/button>\r\n                                <button class=\"pth-btn pth-btn-primary pth-btn-small\" id=\"pth-crop-keep-btn\" title=\"Keeps only the highlighted part\" aria-label=\"Keep only selected region\">\ud83c\udfb5 Keep Only Selected<\/button>\r\n                                <button class=\"pth-btn pth-btn-secondary pth-btn-small\" id=\"pth-deselect-btn\" title=\"Clear highlight\" aria-label=\"Clear selection\">\ud83d\udd04 Deselect<\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div style=\"display:grid; grid-template-columns:1fr 1fr; gap:30px; margin-bottom:25px; padding:20px; background:#f8fafc; border-radius:12px; border:1px solid #e2e8f0;\">\r\n                            <div>\r\n                                <div class=\"pth-range-label\"><span>\ud83d\udd0a Volume<\/span> <span id=\"pth-nv-vol-val\" style=\"color:#3b82f6;\">100%<\/span><\/div>\r\n                                <input type=\"range\" class=\"pth-range-input\" id=\"pth-slider-volume\" min=\"0\" max=\"2\" step=\"0.05\" value=\"1\" aria-label=\"Volume level\">\r\n                            <\/div>\r\n                            <div>\r\n                                <div class=\"pth-range-label\"><span>\u23e9 Speed<\/span> <span id=\"pth-speed-val\" style=\"color:#3b82f6;\">1.0x<\/span><\/div>\r\n                                <input type=\"range\" class=\"pth-range-input\" id=\"pth-slider-speed\" min=\"0.5\" max=\"2\" step=\"0.05\" value=\"1\" aria-label=\"Playback speed (also affects pitch)\">\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div style=\"margin-bottom:30px;\">\r\n                            <label class=\"pth-label\" style=\"font-size: 0.95rem;\">\u26a1 AI Quick Presets<\/label>\r\n                            <div style=\"display:flex; gap:10px; flex-wrap:wrap;\">\r\n                                <button class=\"pth-btn pth-btn-secondary pth-btn-small\" data-preset=\"voice\">Voice Clean<\/button>\r\n                                <button class=\"pth-btn pth-btn-secondary pth-btn-small\" data-preset=\"podcast\">Podcast Ready<\/button>\r\n                                <button class=\"pth-btn pth-btn-secondary pth-btn-small\" data-preset=\"karaoke\">Karaoke Mode<\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <h3 style=\"font-size:1.1rem; color:#1e293b; margin:25px 0 15px 0; padding-top:20px; border-top:2px dashed #e2e8f0;\">\u2728 Advanced DSP Studio Effects<\/h3>\r\n                        <div class=\"pth-tools-grid\">\r\n                            <div class=\"pth-tool-box\"><div class=\"pth-tool-icon\">\ud83e\uddf9<\/div><div class=\"pth-tool-title\">Noise Reduction<\/div><div class=\"pth-tool-desc\">Removes hum, hiss & background noise<\/div><div class=\"pth-toggle-wrap\"><span>OFF<\/span><label class=\"pth-switch\"><input type=\"checkbox\" id=\"pth-toggle-noise\" aria-label=\"Toggle noise reduction\"><span class=\"pth-slider\"><\/span><\/label><span style=\"color:#10b981;\">ON<\/span><\/div><\/div>\r\n                            <div class=\"pth-tool-box\"><div class=\"pth-tool-icon\">\ud83d\udde3\ufe0f<\/div><div class=\"pth-tool-title\">Acapella Focus<\/div><div class=\"pth-tool-desc\">Enhances voice, suppresses music<\/div><div class=\"pth-toggle-wrap\"><span>OFF<\/span><label class=\"pth-switch\"><input type=\"checkbox\" id=\"pth-toggle-acapella\" aria-label=\"Toggle acapella focus\"><span class=\"pth-slider\"><\/span><\/label><span style=\"color:#10b981;\">ON<\/span><\/div><\/div>\r\n                            <div class=\"pth-tool-box\"><div class=\"pth-tool-icon\">\ud83c\udfa4<\/div><div class=\"pth-tool-title\">Vocal Cut (Karaoke)<\/div><div class=\"pth-tool-desc\">Isolates instrumental track<\/div><div class=\"pth-toggle-wrap\"><span>OFF<\/span><label class=\"pth-switch\"><input type=\"checkbox\" id=\"pth-toggle-karaoke\" aria-label=\"Toggle vocal cut karaoke\"><span class=\"pth-slider\"><\/span><\/label><span style=\"color:#10b981;\">ON<\/span><\/div><\/div>\r\n                            <div class=\"pth-tool-box\"><div class=\"pth-tool-icon\">\u23ea<\/div><div class=\"pth-tool-title\">Reverse Audio<\/div><div class=\"pth-tool-desc\">Plays the whole track backwards<\/div><div class=\"pth-toggle-wrap\"><span>OFF<\/span><label class=\"pth-switch\"><input type=\"checkbox\" id=\"pth-toggle-reverse\" aria-label=\"Toggle reverse audio\"><span class=\"pth-slider\"><\/span><\/label><span style=\"color:#10b981;\">ON<\/span><\/div><\/div>\r\n                        <\/div>\r\n\r\n                        <div style=\"background:#f8fafc; border:1px solid #e2e8f0; padding:20px; border-radius:12px; margin-bottom:25px;\">\r\n                            <h4 style=\"margin:0 0 20px 0; font-size:1.05rem; color:#1e293b; text-align:center;\">\ud83c\udf9a\ufe0f 3-Band Graphic EQ + Studio Compressor<\/h4>\r\n                            <div class=\"pth-eq-grid\">\r\n                                <div class=\"pth-eq-slider-wrap\">\r\n                                    <span class=\"pth-eq-label\">Bass<\/span>\r\n                                    <input type=\"range\" class=\"pth-range-vertical\" id=\"pth-eq-bass\" min=\"-12\" max=\"12\" value=\"0\" aria-label=\"Bass gain in decibels\">\r\n                                    <span class=\"pth-eq-value\">-12 \/ +12 dB<\/span>\r\n                                <\/div>\r\n                                <div class=\"pth-eq-slider-wrap\">\r\n                                    <span class=\"pth-eq-label\">Mid<\/span>\r\n                                    <input type=\"range\" class=\"pth-range-vertical\" id=\"pth-eq-mid\" min=\"-12\" max=\"12\" value=\"0\" aria-label=\"Mid gain in decibels\">\r\n                                    <span class=\"pth-eq-value\">-12 \/ +12 dB<\/span>\r\n                                <\/div>\r\n                                <div class=\"pth-eq-slider-wrap\">\r\n                                    <span class=\"pth-eq-label\">Treble<\/span>\r\n                                    <input type=\"range\" class=\"pth-range-vertical\" id=\"pth-eq-treble\" min=\"-12\" max=\"12\" value=\"0\" aria-label=\"Treble gain in decibels\">\r\n                                    <span class=\"pth-eq-value\">-12 \/ +12 dB<\/span>\r\n                                <\/div>\r\n                            <\/div>\r\n                            <div class=\"pth-toggle-wrap\" style=\"padding-top:15px; border-top:1px dashed #cbd5e1;\">\r\n                                <span style=\"font-weight:800;\">Compressor (Radio Ready)<\/span>\r\n                                <label class=\"pth-switch\"><input type=\"checkbox\" id=\"pth-toggle-compressor\" aria-label=\"Toggle radio-ready compressor\"><span class=\"pth-slider\"><\/span><\/label>\r\n                                <span style=\"color:#10b981; font-weight:800;\">ON<\/span>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div style=\"border-top: 2px dashed #e2e8f0; padding-top:25px; margin-top:15px; background: #eff6ff; padding: 20px; border-radius: 12px;\">\r\n                            <label class=\"pth-label\" style=\"color:#1e3a8a;\">Export Format<\/label>\r\n                            <div style=\"display:flex; gap:15px; flex-wrap:wrap; align-items:center;\">\r\n                                <select id=\"pth-export-format\" class=\"pth-select\" style=\"max-width:250px; border-color:#93c5fd;\" aria-label=\"Export file format\">\r\n                                    <option value=\"mp3\">MP3 (Universal Audio)<\/option>\r\n                                    <option value=\"wav\">WAV (Lossless Studio)<\/option>\r\n                                <\/select>\r\n                                <select id=\"pth-nv-bitrate\" class=\"pth-select\" style=\"max-width:200px; border-color:#93c5fd;\" aria-label=\"MP3 quality bitrate\" title=\"MP3 quality (higher = larger file)\">\r\n                                    <option value=\"128\">128 kbps (Smallest)<\/option>\r\n                                    <option value=\"192\" selected>192 kbps (Balanced)<\/option>\r\n                                    <option value=\"320\">320 kbps (Best Quality)<\/option>\r\n                                <\/select>\r\n                                <button class=\"pth-btn pth-btn-success\" id=\"pth-nv-process-btn\" style=\"flex:1; box-shadow: 0 6px 15px rgba(16,185,129,0.4);\" aria-label=\"Apply filters and download master audio\">\r\n                                    \u26a1 Apply Filters & Download Master\r\n                                <\/button>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <div class=\"pth-tab-pane\" id=\"tab-joiner\">\r\n                <div id=\"pth-joiner-main-view\" class=\"pth-card\" style=\"border-top: 4px solid #8b5cf6;\">\r\n                    <h2 class=\"pth-card-title\">\ud83d\udd17 Audio Joiner \/ Merger<\/h2>\r\n                    <p style=\"font-size:0.95rem; color:#64748b; margin-bottom: 20px;\">Drop multiple files below. You can trim each file individually before merging them into a single track.<\/p>\r\n                    \r\n                    <div class=\"pth-drop-zone\" id=\"pth-joiner-dz\" style=\"border-color:#a855f7; background:#faf5ff;\">\r\n                        <div class=\"pth-dz-icon\" style=\"color:#9333ea;\">\u2795<\/div>\r\n                        <h3 class=\"pth-dz-text\" style=\"color:#6b21a8;\">Add Files to Join<\/h3>\r\n                        <p class=\"pth-dz-sub\">Drag & Drop Multiple Files Here<\/p>\r\n                    <\/div>\r\n                    <input type=\"file\" id=\"pth-joiner-file\" accept=\"audio\/*, video\/*\" multiple style=\"display:none;\">\r\n\r\n                    <ul class=\"pth-joiner-list\" id=\"pth-joiner-list\" style=\"display:none;\"><\/ul>\r\n                    \r\n                    <div id=\"pth-joiner-actions\" style=\"display:none; background:#f8fafc; padding:20px; border-radius:12px; border:1px solid #e2e8f0; margin-top:20px;\">\r\n                        \r\n                        <div style=\"margin-bottom: 20px; border-bottom: 1px dashed #cbd5e1; padding-bottom: 15px; display: none;\" id=\"pth-joiner-preview-wrap\">\r\n                            <label class=\"pth-label\" style=\"color:#0f172a;\">Preview Merged Audio:<\/label>\r\n                            <audio id=\"pth-joiner-player\" controls style=\"width: 100%; height: 40px; border-radius:6px;\"><\/audio>\r\n                        <\/div>\r\n\r\n                        <label class=\"pth-label\" style=\"text-align:left;\">Output Format:<\/label>\r\n                        <select id=\"pth-joiner-format\" class=\"pth-select\" style=\"margin-bottom:15px;\">\r\n                            <option value=\"mp3\">MP3 (Universal Audio)<\/option>\r\n                            <option value=\"wav\">WAV (Lossless)<\/option>\r\n                        <\/select>\r\n                        <div style=\"display:flex; gap:10px; flex-wrap:wrap;\">\r\n                            <button class=\"pth-btn pth-btn-primary\" id=\"pth-join-btn\" style=\"flex:1; font-size:1.1rem; padding:16px; background:linear-gradient(135deg, #8b5cf6, #6d28d9);\">\ud83d\udd17 Merge Audio Files<\/button>\r\n                            <button class=\"pth-btn pth-btn-success\" id=\"pth-join-dl-btn\" style=\"display:none; flex:1; font-size:1.1rem; padding:16px;\">\ud83d\udce5 Download Merged File<\/button>\r\n                        <\/div>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div id=\"pth-joiner-editor-view\" class=\"pth-card\" style=\"display:none; border-top: 4px solid #f43f5e;\">\r\n                    <div style=\"display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;\">\r\n                        <h2 class=\"pth-card-title\" style=\"margin:0; border:none; padding:0;\">\u2702\ufe0f Trimming: <span id=\"pth-joiner-edit-name\" style=\"font-weight:600; color:#f43f5e; margin-left:8px;\"><\/span><\/h2>\r\n                    <\/div>\r\n                    <p style=\"font-size:0.85rem; color:#64748b;\">Highlight the portion you want to KEEP for the final merged file.<\/p>\r\n                    \r\n                    <div class=\"pth-dark-player-box\" style=\"margin-top:15px;\">\r\n                        <div class=\"pth-waveform-container\" id=\"pth-joiner-waveform\" style=\"border:none; margin-bottom:0; box-shadow:none; height:100px;\"><\/div>\r\n                        <div class=\"pth-timeline-container\" id=\"pth-joiner-timeline\"><\/div>\r\n                    <\/div>\r\n\r\n                    <div class=\"pth-playback-controls\" style=\"margin-top:15px; border:none; box-shadow:none; background:transparent; padding:0;\">\r\n                        <button class=\"pth-ctrl-btn\" id=\"pth-joiner-btn-stop\" title=\"Stop\"><div class=\"pth-css-stop\"><\/div><\/button>\r\n                        <button class=\"pth-play-btn\" id=\"pth-joiner-btn-play\" style=\"width:55px; height:55px; background:linear-gradient(135deg, #f43f5e, #be123c);\">\r\n                            <div class=\"pth-css-play\" id=\"pth-joiner-icon-play\"><\/div>\r\n                            <div class=\"pth-css-pause\" id=\"pth-joiner-icon-pause\"><\/div>\r\n                        <\/button>\r\n                    <\/div>\r\n\r\n                    <div style=\"display:flex; gap:10px; justify-content:flex-end; border-top:1px solid #e2e8f0; padding-top:15px; margin-top:15px;\">\r\n                        <button class=\"pth-btn pth-btn-secondary\" id=\"pth-joiner-deselect-btn\">\ud83d\udd04 Deselect<\/button>\r\n                        <button class=\"pth-btn pth-btn-secondary\" id=\"pth-joiner-cancel-trim\">Cancel<\/button>\r\n                        <button class=\"pth-btn pth-btn-success\" id=\"pth-joiner-save-trim\">\ud83d\udcbe Save Trim Region<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n            <div class=\"pth-tab-pane\" id=\"tab-converter\">\r\n                <div class=\"pth-card\" style=\"border-top: 4px solid #0ea5e9;\">\r\n                    <h2 class=\"pth-card-title\">\ud83d\udd04 Audio Format Converter<\/h2>\r\n                    <div style=\"background:#f8fafc; padding:20px; border-radius:12px; border:1px solid #e2e8f0; margin-bottom:20px;\">\r\n                        <label class=\"pth-label\">1. Select Audio File:<\/label>\r\n                        <input type=\"file\" id=\"pth-conv-file\" accept=\"audio\/*\" style=\"width:100%; padding:10px; background:#fff; border:1px solid #cbd5e1; border-radius:6px; margin-bottom:20px;\">\r\n                        \r\n                        <label class=\"pth-label\">2. Target Format:<\/label>\r\n                        <select id=\"pth-conv-target\" style=\"width:100%; padding:14px; border-radius:8px; border:2px solid #cbd5e1; font-weight:700; margin-bottom:20px;\">\r\n                            <option value=\"mp3\">MP3 (Standard Audio)<\/option>\r\n                            <option value=\"wav\">WAV (Uncompressed Studio)<\/option>\r\n                            <option value=\"webm\">WebM (Browser Audio)<\/option>\r\n                            <option value=\"ogg\">OGG (Android\/Web)<\/option>\r\n                        <\/select>\r\n\r\n                        <button class=\"pth-btn pth-btn-primary\" id=\"pth-convert-btn\" style=\"width:100%; font-size: 1.1rem; padding: 15px;\">\ud83d\udd04 Convert Now<\/button>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n\r\n        <\/div>\r\n\r\n        <div class=\"pth-sidebar-col\" id=\"pth-audio-studio-sidebar\">\r\n            <div class=\"pth-card\" style=\"background:#f0fdf4; border-color:#6ee7b7; padding:24px;\">\r\n                <h3 class=\"pth-card-title\" style=\"font-size:1.15rem; color:#065f46; border-bottom:1px solid #a7f3d0; padding-bottom:12px;\">\ud83d\udd12 100% Offline Multi-Tool<\/h3>\r\n                <p style=\"font-size:0.95rem; color:#047857; line-height:1.65; margin:0;\">All Studio, Splitter, Converter, and Joiner functions run entirely in your browser using the Web Audio API. <strong>Zero uploads to any server<\/strong>.<\/p>\r\n            <\/div>\r\n            <div class=\"pth-card\" style=\"padding:24px;\">\r\n                <h3 class=\"pth-card-title\" style=\"font-size:1.15rem;\">\ud83d\udca1 Pro Studio Tips<\/h3>\r\n                <ul style=\"font-size:0.9rem; line-height:1.7; padding-left:0; list-style:none; margin:0; color:#475569;\">\r\n                    <li style=\"margin-bottom:14px;\"><strong>Video Support:<\/strong> You can upload MP4\/WebM videos. The tool will automatically extract and let you edit the audio track!<\/li>\r\n                    <li style=\"margin-bottom:14px;\"><strong>Delete Mid Part:<\/strong> Drag a selection on the waveform and click <strong style=\"color:#dc2626;\">Delete Selected<\/strong> to remove the middle section seamlessly.<\/li>\r\n                    <li><strong>Real MP3 Export:<\/strong> This tool utilizes a native LameJS encoder to provide true offline MP3 conversion!<\/li>\r\n                <\/ul>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n<\/div>\r\n\r\n<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function() {\r\n    window.pthAudioMultiToolApp = {\r\n        initialized: false,\r\n        wavesurfer: null, wsRegions: null,\r\n        originalBuffer: null, originalFile: null,\r\n        mediaRecorder: null, audioChunks: [], recordingTimer: null, recSeconds: 0,\r\n        \r\n        \/\/ Joiner State\r\n        joinerFiles: [], joinedBlob: null,\r\n        joinerWavesurfer: null, joinerWsRegions: null, currentJoinerEditIndex: -1,\r\n        \r\n        init: function() {\r\n            this.cacheDOM();\r\n            if(!this.UI.layout) return; \r\n            this.attachEvents();\r\n        },\r\n        \r\n        cacheDOM: function() {\r\n            const get = id => document.getElementById(id);\r\n            this.UI = {\r\n                layout: get('pth-audio-studio-layout'),\r\n                sidebar: get('pth-audio-studio-sidebar'),\r\n                fwBtn: get('pth-fw-btn-audio'),\r\n                \r\n                tabBtns: document.querySelectorAll('.pth-tab-btn'),\r\n                tabPanes: document.querySelectorAll('.pth-tab-pane'),\r\n                \r\n                initialLoad: get('pth-initial-load'),\r\n                uploadArea: get('pth-upload-area'),\r\n                dz: get('pth-audio-dz'),\r\n                fileIn: get('pth-audio-file'),\r\n                workspace: get('pth-workspace'),\r\n                fileName: get('pth-file-name'),\r\n                closeBtn: get('pth-close-workspace'),\r\n                videoPreview: get('pth-video-preview'),\r\n                waveform: get('pth-waveform'),\r\n                timeline: get('pth-timeline'),\r\n                timeDisplay: get('pth-time-display'),\r\n                \r\n                btnPlay: get('pth-btn-play'),\r\n                iconPlay: get('pth-icon-play'),\r\n                iconPause: get('pth-icon-pause'),\r\n                btnStop: get('pth-btn-stop'),\r\n                btnBack: get('pth-btn-backward'),\r\n                btnFwd: get('pth-btn-forward'),\r\n                cutMidBtn: get('pth-cut-mid-btn'),\r\n                cropKeepBtn: get('pth-crop-keep-btn'),\r\n                deselectBtn: get('pth-deselect-btn'),\r\n                \r\n                togNoise: get('pth-toggle-noise'),\r\n                togAcapella: get('pth-toggle-acapella'),\r\n                togKaraoke: get('pth-toggle-karaoke'),\r\n                togReverse: get('pth-toggle-reverse'),\r\n                exportFormat: get('pth-export-format'),\r\n                bitrate: get('pth-nv-bitrate'),\r\n                processBtn: get('pth-nv-process-btn'),\r\n                renderOverlay: get('pth-render-overlay'),\r\n                renderBar: get('pth-render-bar'),\r\n                sliderVol: get('pth-slider-volume'),\r\n                valVol: get('pth-nv-vol-val'),\r\n                sliderSpeed: get('pth-slider-speed'),\r\n                valSpeed: get('pth-speed-val'),\r\n                \r\n                togCompressor: get('pth-toggle-compressor'),\r\n                eqBass: get('pth-eq-bass'),\r\n                eqMid: get('pth-eq-mid'),\r\n                eqTreble: get('pth-eq-treble'),\r\n                \r\n                recordBtn: get('pth-record-btn'),\r\n                stopRecordBtn: get('pth-stop-record-btn'),\r\n                recordingStatus: get('pth-recording-status'),\r\n                recTime: get('pth-rec-time'),\r\n                \r\n                \/\/ Workspace Rec Buttons\r\n                wsRecordBtn: get('pth-ws-record-btn'),\r\n                wsStopRecordBtn: get('pth-ws-stop-record-btn'),\r\n                wsRecordingStatus: get('pth-ws-recording-status'),\r\n                wsRecTime: get('pth-ws-rec-time'),\r\n                \r\n                joinDz: get('pth-joiner-dz'),\r\n                joinFileIn: get('pth-joiner-file'),\r\n                joinList: get('pth-joiner-list'),\r\n                joinActions: get('pth-joiner-actions'),\r\n                joinBtn: get('pth-join-btn'),\r\n                joinDlBtn: get('pth-join-dl-btn'),\r\n                joinFormat: get('pth-joiner-format'),\r\n                joinPlayerWrap: get('pth-joiner-preview-wrap'),\r\n                joinPlayer: get('pth-joiner-player'),\r\n                joinMainView: get('pth-joiner-main-view'),\r\n                \r\n                joinEditorView: get('pth-joiner-editor-view'),\r\n                joinEditName: get('pth-joiner-edit-name'),\r\n                joinWaveform: get('pth-joiner-waveform'),\r\n                joinTimeline: get('pth-joiner-timeline'),\r\n                joinBtnPlay: get('pth-joiner-btn-play'),\r\n                joinIconPlay: get('pth-joiner-icon-play'),\r\n                joinIconPause: get('pth-joiner-icon-pause'),\r\n                joinBtnStop: get('pth-joiner-btn-stop'),\r\n                joinSaveTrimBtn: get('pth-joiner-save-trim'),\r\n                joinCancelTrimBtn: get('pth-joiner-cancel-trim'),\r\n                joinDeselectBtn: get('pth-joiner-deselect-btn'),\r\n                \r\n                convFile: get('pth-conv-file'),\r\n                convTarget: get('pth-conv-target'),\r\n                convertBtn: get('pth-convert-btn'),\r\n                \r\n                toast: get('pth-aud-toast')\r\n            };\r\n        },\r\n\r\n        showToast: function(msg, isError = false) {\r\n            if (!this.UI.toast) return;\r\n            this.UI.toast.innerHTML = isError ? '\\u274c ' + msg : '\\u2705 ' + msg;\r\n            this.UI.toast.style.background = isError ? '#ef4444' : '#1e293b';\r\n            this.UI.toast.classList.add('show');\r\n            setTimeout(() => { this.UI.toast.classList.remove('show'); }, 3000);\r\n        },\r\n\r\n        toggleWidth: function() {\r\n            if (this.UI.layout.classList.contains('pth-full-width')) {\r\n                this.UI.layout.classList.remove('pth-full-width');\r\n                if(this.UI.sidebar) this.UI.sidebar.style.display = 'block';\r\n                if(this.UI.fwBtn) this.UI.fwBtn.innerHTML = '\\u26f6 Full Width';\r\n            } else {\r\n                this.UI.layout.classList.add('pth-full-width');\r\n                if(this.UI.sidebar) this.UI.sidebar.style.display = 'none';\r\n                if(this.UI.fwBtn) this.UI.fwBtn.innerHTML = '\\u25c0 Split View';\r\n            }\r\n            if(this.wavesurfer) setTimeout(() => this.wavesurfer.redraw(), 100);\r\n            if(this.joinerWavesurfer) setTimeout(() => this.joinerWavesurfer.redraw(), 100);\r\n        },\r\n\r\n        formatTime: function(seconds) {\r\n            if (isNaN(seconds)) return '00:00';\r\n            const m = Math.floor(seconds \/ 60);\r\n            const s = Math.floor(seconds % 60);\r\n            return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);\r\n        },\r\n\r\n        attachEvents: function() {\r\n            const self = this;\r\n            if(this.UI.fwBtn) this.UI.fwBtn.addEventListener('click', () => self.toggleWidth());\r\n            \r\n            this.UI.tabBtns.forEach(btn => {\r\n                btn.addEventListener('click', function() {\r\n                    self.UI.tabBtns.forEach(b => b.classList.remove('active'));\r\n                    self.UI.tabPanes.forEach(p => p.classList.remove('active'));\r\n                    this.classList.add('active');\r\n                    const target = document.getElementById(this.dataset.target);\r\n                    if(target) target.classList.add('active');\r\n                    if(self.wavesurfer) setTimeout(() => self.wavesurfer.redraw(), 50);\r\n                });\r\n            });\r\n\r\n            \/\/ ====== TAB 1: STUDIO EVENTS ======\r\n            if(this.UI.closeBtn) this.UI.closeBtn.addEventListener('click', () => self.resetStudio());\r\n            \r\n            if(this.UI.dz) {\r\n                this.UI.dz.addEventListener('click', () => { if(self.UI.fileIn) self.UI.fileIn.click(); });\r\n                this.UI.dz.addEventListener('dragover', e => { e.preventDefault(); self.UI.dz.style.borderColor = '#1e40af'; });\r\n                this.UI.dz.addEventListener('dragleave', () => self.UI.dz.style.borderColor = '#3b82f6');\r\n                this.UI.dz.addEventListener('drop', e => { \r\n                    e.preventDefault(); \r\n                    self.UI.dz.style.borderColor = '#3b82f6'; \r\n                    if (e.dataTransfer.files.length) self.loadAudioFile(e.dataTransfer.files[0]); \r\n                });\r\n            }\r\n            if(this.UI.fileIn) this.UI.fileIn.addEventListener('change', e => { if (e.target.files.length) self.loadAudioFile(e.target.files[0]); });\r\n            \r\n            \/\/ Playback\r\n            if(this.UI.btnPlay) this.UI.btnPlay.addEventListener('click', () => { if(self.wavesurfer) self.wavesurfer.playPause(); });\r\n            if(this.UI.btnStop) this.UI.btnStop.addEventListener('click', () => { if(self.wavesurfer) self.wavesurfer.stop(); });\r\n            if(this.UI.btnBack) this.UI.btnBack.addEventListener('click', () => { if(self.wavesurfer) self.wavesurfer.skip(-5); });\r\n            if(this.UI.btnFwd) this.UI.btnFwd.addEventListener('click', () => { if(self.wavesurfer) self.wavesurfer.skip(5); });\r\n            \r\n            \/\/ Split Region Actions\r\n            if(this.UI.cutMidBtn) this.UI.cutMidBtn.addEventListener('click', () => self.cutSelectedRegion(true)); \r\n            if(this.UI.cropKeepBtn) this.UI.cropKeepBtn.addEventListener('click', () => self.cutSelectedRegion(false)); \r\n            if(this.UI.deselectBtn) this.UI.deselectBtn.addEventListener('click', () => { if(self.wsRegions) self.wsRegions.clearRegions(); });\r\n            \r\n            \/\/ Sliders\r\n            if(this.UI.sliderVol) {\r\n                this.UI.sliderVol.addEventListener('input', e => {\r\n                    const val = parseFloat(e.target.value);\r\n                    self.UI.valVol.textContent = Math.round(val * 100) + '%';\r\n                    if(self.wavesurfer) self.wavesurfer.setVolume(val);\r\n                });\r\n            }\r\n            if(this.UI.sliderSpeed) {\r\n                this.UI.sliderSpeed.addEventListener('input', e => {\r\n                    const val = parseFloat(e.target.value);\r\n                    self.UI.valSpeed.textContent = val.toFixed(1) + 'x';\r\n                    if(self.wavesurfer) self.wavesurfer.setPlaybackRate(val);\r\n                });\r\n            }\r\n\r\n            \/\/ Mic Record\r\n            if(this.UI.recordBtn) this.UI.recordBtn.addEventListener('click', () => self.startRecording());\r\n            if(this.UI.stopRecordBtn) this.UI.stopRecordBtn.addEventListener('click', () => self.stopRecording());\r\n            if(this.UI.wsRecordBtn) this.UI.wsRecordBtn.addEventListener('click', () => self.startRecording());\r\n            if(this.UI.wsStopRecordBtn) this.UI.wsStopRecordBtn.addEventListener('click', () => self.stopRecording());\r\n            \r\n            \/\/ DSP & Export\r\n            if(this.UI.togAcapella) this.UI.togAcapella.addEventListener('change', e => { if(e.target.checked && self.UI.togKaraoke) self.UI.togKaraoke.checked = false; });\r\n            if(this.UI.togKaraoke) this.UI.togKaraoke.addEventListener('change', e => { if(e.target.checked && self.UI.togAcapella) self.UI.togAcapella.checked = false; });\r\n            if(this.UI.processBtn) this.UI.processBtn.addEventListener('click', () => self.renderAndDownload());\r\n\r\n            document.querySelectorAll('[data-preset]').forEach(btn => {\r\n                btn.addEventListener('click', () => self.applyPreset(btn.dataset.preset));\r\n            });\r\n\r\n            \/\/ ====== TAB 2: JOINER EVENTS ======\r\n            if(this.UI.joinDz) {\r\n                this.UI.joinDz.addEventListener('click', () => { if(self.UI.joinFileIn) self.UI.joinFileIn.click(); });\r\n                this.UI.joinDz.addEventListener('dragover', e => { e.preventDefault(); self.UI.joinDz.style.borderColor = '#6d28d9'; });\r\n                this.UI.joinDz.addEventListener('dragleave', () => self.UI.joinDz.style.borderColor = '#8b5cf6');\r\n                this.UI.joinDz.addEventListener('drop', e => { e.preventDefault(); self.UI.joinDz.style.borderColor = '#8b5cf6'; if (e.dataTransfer.files.length) self.addJoinerFiles(e.dataTransfer.files); });\r\n            }\r\n            if(this.UI.joinFileIn) this.UI.joinFileIn.addEventListener('change', e => { if (e.target.files.length) self.addJoinerFiles(e.target.files); });\r\n            if(this.UI.joinBtn) this.UI.joinBtn.addEventListener('click', () => self.joinAudioFiles());\r\n            if(this.UI.joinDlBtn) this.UI.joinDlBtn.addEventListener('click', () => self.downloadJoinedFile());\r\n            \r\n            \/\/ Joiner Editor Events\r\n            if(this.UI.joinBtnPlay) this.UI.joinBtnPlay.addEventListener('click', () => { if(self.joinerWavesurfer) self.joinerWavesurfer.playPause(); });\r\n            if(this.UI.joinBtnStop) this.UI.joinBtnStop.addEventListener('click', () => { if(self.joinerWavesurfer) self.joinerWavesurfer.stop(); });\r\n            if(this.UI.joinSaveTrimBtn) this.UI.joinSaveTrimBtn.addEventListener('click', () => self.saveJoinerTrim());\r\n            if(this.UI.joinCancelTrimBtn) this.UI.joinCancelTrimBtn.addEventListener('click', () => self.closeJoinerEditor());\r\n            if(this.UI.joinDeselectBtn) this.UI.joinDeselectBtn.addEventListener('click', () => { if(self.joinerWsRegions) self.joinerWsRegions.clearRegions(); });\r\n\r\n            \/\/ ====== TAB 3: CONVERTER EVENTS ======\r\n            if(this.UI.convertBtn) this.UI.convertBtn.addEventListener('click', () => self.convertAudio());\r\n            \r\n            \/\/ Global scope for onclick events\r\n            window.pthAudioMultiToolApp = this;\r\n        },\r\n\r\n        \/\/ --- LAME JS MP3 ENCODER FUNCTION ---\r\n        bufferToMp3Blob: function(audioBuffer) {\r\n            if (typeof lamejs === 'undefined') throw new Error(\"LameJS library not found. MP3 export unavailable.\");\r\n            const channels = audioBuffer.numberOfChannels;\r\n            const sampleRate = audioBuffer.sampleRate;\r\n            let kbps = 192;\r\n            if (this.UI && this.UI.bitrate) {\r\n                const parsed = parseInt(this.UI.bitrate.value, 10);\r\n                if (parsed === 128 || parsed === 192 || parsed === 320) kbps = parsed;\r\n            }\r\n            const mp3encoder = new lamejs.Mp3Encoder(channels, sampleRate, kbps); \r\n            const mp3Data = [];\r\n\r\n            const left = audioBuffer.getChannelData(0);\r\n            const right = channels > 1 ? audioBuffer.getChannelData(1) : left;\r\n            \r\n            const sampleBlockSize = 1152; \r\n            for (let i = 0; i < left.length; i += sampleBlockSize) {\r\n                const leftChunk = left.subarray(i, i + sampleBlockSize);\r\n                const rightChunk = right.subarray(i, i + sampleBlockSize);\r\n                \r\n                const leftInt = new Int16Array(leftChunk.length);\r\n                const rightInt = new Int16Array(rightChunk.length);\r\n                \r\n                for(let j=0; j<leftChunk.length; j++) {\r\n                    leftInt[j] = leftChunk[j] < 0 ? leftChunk[j] * 32768 : leftChunk[j] * 32767;\r\n                    rightInt[j] = rightChunk[j] < 0 ? rightChunk[j] * 32768 : rightChunk[j] * 32767;\r\n                }\r\n                const mp3buf = channels === 2 ? mp3encoder.encodeBuffer(leftInt, rightInt) : mp3encoder.encodeBuffer(leftInt);\r\n                if (mp3buf.length > 0) mp3Data.push(mp3buf);\r\n            }\r\n            const mp3buf = mp3encoder.flush();\r\n            if (mp3buf.length > 0) mp3Data.push(mp3buf);\r\n\r\n            return new Blob(mp3Data, {type: 'audio\/mp3'});\r\n        },\r\n\r\n        bufferToWavBlob: function(audioBuffer) {\r\n            const numOfChan = audioBuffer.numberOfChannels, length = audioBuffer.length * numOfChan * 2 + 44;\r\n            const buffer = new ArrayBuffer(length), view = new DataView(buffer);\r\n            let pos = 0, offset = 0; const channels = [];\r\n            for (let i = 0; i < numOfChan; i++) channels.push(audioBuffer.getChannelData(i));\r\n            \r\n            function setUint32(data) { view.setUint32(pos, data, true); pos += 4; }\r\n            function setUint16(data) { view.setUint16(pos, data, true); pos += 2; }\r\n            \r\n            setUint32(0x46464952); setUint32(length - 8); setUint32(0x45564157); setUint32(0x20746d66); setUint32(16); setUint16(1); setUint16(numOfChan);\r\n            setUint32(audioBuffer.sampleRate); setUint32(audioBuffer.sampleRate * 2 * numOfChan); setUint16(numOfChan * 2); setUint16(16);\r\n            setUint32(0x61746164); setUint32(length - pos - 4);\r\n            \r\n            while (pos < length) {\r\n                for (let i = 0; i < numOfChan; i++) {\r\n                    const sample = Math.max(-1, Math.min(1, channels[i][offset]));\r\n                    view.setInt16(pos, sample < 0 ? sample * 0x8000 : sample * 0x7FFF, true); pos += 2;\r\n                }\r\n                offset++;\r\n            }\r\n            return new Blob([buffer], { type: \"audio\/wav\" });\r\n        },\r\n\r\n        \/\/ --- STUDIO FUNCTIONS ---\r\n        loadAudioFile: function(file, name = null) {\r\n            try {\r\n                const fileName = name || file.name || 'recording.webm';\r\n                const isAudioType = file.type && file.type.startsWith('audio\/');\r\n                const isVideoType = file.type && file.type.startsWith('video\/');\r\n                const hasExt = fileName.match(\/\\.(mp3|wav|ogg|m4a|webm|flac|aac|mp4|mov|avi)$\/i);\r\n                \r\n                if (!isAudioType && !isVideoType && !hasExt) {\r\n                    return this.showToast('Invalid file format. Please upload an audio or video file.', true);\r\n                }\r\n                \r\n                this.originalFile = file;\r\n                if(this.UI.fileName) this.UI.fileName.textContent = fileName;\r\n                \r\n                if(this.UI.uploadArea) this.UI.uploadArea.style.display = 'none';\r\n                if(this.UI.initialLoad) this.UI.initialLoad.style.display = 'flex'; \r\n                \r\n                this.initWavesurfer(file);\r\n            } catch(err) {\r\n                this.showToast('Error loading file.', true);\r\n            }\r\n        },\r\n\r\n        initWavesurfer: function(file) {\r\n            const self = this;\r\n            if (typeof WaveSurfer === 'undefined') return this.showToast('Audio libraries loading, please try again in a moment.', true);\r\n            \r\n            if(this.wavesurfer) this.wavesurfer.destroy();\r\n            if(this.UI.timeline) this.UI.timeline.innerHTML = '';\r\n            \r\n            const isVideo = file.type && file.type.startsWith('video\/');\r\n            const objectUrl = URL.createObjectURL(file);\r\n            let mediaEl = null;\r\n\r\n            if(isVideo && this.UI.videoPreview) {\r\n                this.UI.videoPreview.style.display = 'block';\r\n                this.UI.videoPreview.src = objectUrl;\r\n                mediaEl = this.UI.videoPreview;\r\n                this.showToast('Video loaded! Audio extracted for editing.');\r\n            } else if (this.UI.videoPreview) {\r\n                this.UI.videoPreview.style.display = 'none';\r\n                this.UI.videoPreview.src = '';\r\n            }\r\n\r\n            try {\r\n                this.wavesurfer = WaveSurfer.create({\r\n                    container: '#pth-waveform',\r\n                    waveColor: '#60a5fa', progressColor: '#2563eb', cursorColor: '#ffffff',\r\n                    barWidth: 2, barGap: 1, barRadius: 2, height: 100, normalize: true,\r\n                    media: mediaEl,\r\n                    plugins: [\r\n                        window.WaveSurfer.Timeline.create({\r\n                            container: '#pth-timeline',\r\n                            height: 20, timeInterval: 0.1, primaryLabelInterval: 1,\r\n                            style: { fontSize: '11px', color: '#94a3b8' }\r\n                        })\r\n                    ]\r\n                });\r\n                \r\n                if (window.WaveSurfer.Regions) {\r\n                    this.wsRegions = this.wavesurfer.registerPlugin(window.WaveSurfer.Regions.create());\r\n                    this.wsRegions.enableDragSelection({ color: 'rgba(239, 68, 68, 0.4)' });\r\n                }\r\n\r\n                if(!mediaEl) this.wavesurfer.load(objectUrl);\r\n                \r\n                this.wavesurfer.on('ready', () => {\r\n                    if(self.UI.initialLoad) self.UI.initialLoad.style.display = 'none';\r\n                    if(self.UI.workspace) self.UI.workspace.style.display = 'block';\r\n                    if(self.UI.closeBtn) self.UI.closeBtn.style.display = 'inline-flex';\r\n                    if(!isVideo) self.showToast('Audio loaded successfully!');\r\n                    self.setupWebAudio(file);\r\n                });\r\n                \r\n                this.wavesurfer.on('audioprocess', time => { \r\n                    if(self.UI.timeDisplay) self.UI.timeDisplay.textContent = self.formatTime(time) + '.' + Math.floor((time % 1) * 10); \r\n                });\r\n                this.wavesurfer.on('play', () => { \r\n                    if(self.UI.iconPlay) self.UI.iconPlay.style.display = 'none'; \r\n                    if(self.UI.iconPause) self.UI.iconPause.style.display = 'block'; \r\n                });\r\n                this.wavesurfer.on('pause', () => { \r\n                    if(self.UI.iconPlay) self.UI.iconPlay.style.display = 'block'; \r\n                    if(self.UI.iconPause) self.UI.iconPause.style.display = 'none'; \r\n                });\r\n            } catch(e) {\r\n                if(self.UI.initialLoad) self.UI.initialLoad.style.display = 'none';\r\n                this.showToast('Player initialization failed. Please reload.', true);\r\n            }\r\n        },\r\n\r\n        setupWebAudio: function(file) {\r\n            const self = this;\r\n            const reader = new FileReader();\r\n            reader.onload = function(e) {\r\n                try {\r\n                    const AudioContext = window.AudioContext || window.webkitAudioContext;\r\n                    const audioCtx = new AudioContext();\r\n                    audioCtx.decodeAudioData(e.target.result, \r\n                        buffer => { self.originalBuffer = buffer; },\r\n                        err => { self.showToast('Error decoding audio data.', true); }\r\n                    );\r\n                } catch(err) {\r\n                    self.showToast('Audio processing not supported.', true);\r\n                }\r\n            };\r\n            reader.readAsArrayBuffer(file);\r\n        },\r\n\r\n        applyPreset: function(preset) {\r\n            if(!this.UI.togNoise) return;\r\n            if (preset === 'voice') { this.UI.togNoise.checked = true; this.UI.togAcapella.checked = true; this.UI.togKaraoke.checked = false; } \r\n            else if (preset === 'podcast') { this.UI.togNoise.checked = true; this.UI.togAcapella.checked = false; this.UI.togKaraoke.checked = false; } \r\n            else if (preset === 'karaoke') { this.UI.togNoise.checked = true; this.UI.togKaraoke.checked = true; this.UI.togAcapella.checked = false; }\r\n            this.showToast(preset.toUpperCase() + ' preset applied');\r\n        },\r\n\r\n        cutSelectedRegion: function(deleteSelection) {\r\n            if(!this.originalBuffer) return;\r\n            if(!this.wsRegions) return this.showToast('Trimming not available.', true);\r\n            \r\n            const regions = this.wsRegions.getRegions();\r\n            if(regions.length === 0) return this.showToast('Highlight a region on waveform first!', true);\r\n            \r\n            const startSec = regions[0].start;\r\n            const endSec = regions[0].end;\r\n            const sampleRate = this.originalBuffer.sampleRate;\r\n            const channels = this.originalBuffer.numberOfChannels;\r\n            \r\n            const startSample = Math.floor(startSec * sampleRate);\r\n            const endSample = Math.floor(endSec * sampleRate);\r\n            const totalSamples = this.originalBuffer.length;\r\n            \r\n            let newLength;\r\n            if(deleteSelection) {\r\n                newLength = totalSamples - (endSample - startSample);\r\n            } else {\r\n                newLength = endSample - startSample;\r\n            }\r\n\r\n            if(newLength <= 0) return this.showToast('Invalid selection.', true);\r\n\r\n            const AudioContext = window.AudioContext || window.webkitAudioContext;\r\n            const audioCtx = new AudioContext();\r\n            const newBuffer = audioCtx.createBuffer(channels, newLength, sampleRate);\r\n\r\n            for (let i = 0; i < channels; i++) {\r\n                const origData = this.originalBuffer.getChannelData(i);\r\n                const newData = newBuffer.getChannelData(i);\r\n                \r\n                if(deleteSelection) {\r\n                    newData.set(origData.subarray(0, startSample), 0);\r\n                    newData.set(origData.subarray(endSample), startSample);\r\n                } else {\r\n                    newData.set(origData.subarray(startSample, endSample), 0);\r\n                }\r\n            }\r\n\r\n            this.originalBuffer = newBuffer;\r\n            const newBlob = this.bufferToWavBlob(newBuffer);\r\n            const objectUrl = URL.createObjectURL(newBlob);\r\n            \r\n            this.wsRegions.clearRegions();\r\n            if(this.UI.videoPreview) this.UI.videoPreview.style.display = 'none';\r\n            this.wavesurfer.setOptions({ media: null }); \r\n            this.wavesurfer.load(objectUrl);\r\n\r\n            this.showToast(deleteSelection ? 'Middle section removed!' : 'Cropped to selection!');\r\n        },\r\n\r\n        renderAndDownload: async function() {\r\n            if (!this.originalBuffer) return this.showToast('Audio not ready. Please wait.', true);\r\n            if (this.wavesurfer) this.wavesurfer.pause();\r\n            \r\n            if(this.UI.renderOverlay) this.UI.renderOverlay.style.display = 'flex';\r\n            if(this.UI.processBtn) this.UI.processBtn.disabled = true;\r\n            if(this.UI.renderBar) this.UI.renderBar.style.width = '10%';\r\n            \r\n            const self = this;\r\n            setTimeout(async () => {\r\n                try {\r\n                    let start = 0, end = self.originalBuffer.duration;\r\n                    if(self.wsRegions) {\r\n                        let regions = self.wsRegions.getRegions();\r\n                        if (regions.length > 0) { start = regions[0].start; end = regions[0].end; }\r\n                    }\r\n                    \r\n                    const speed = self.UI.sliderSpeed ? parseFloat(self.UI.sliderSpeed.value) : 1;\r\n                    const duration = (end - start) \/ speed;\r\n                    if (duration <= 0) throw new Error('Invalid duration');\r\n\r\n                    const sampleRate = self.originalBuffer.sampleRate;\r\n                    const channels = self.originalBuffer.numberOfChannels;\r\n                    \r\n                    const OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;\r\n                    const offlineCtx = new OfflineAudioContext(channels, duration * sampleRate, sampleRate);\r\n                    const source = offlineCtx.createBufferSource();\r\n                    source.buffer = self.originalBuffer;\r\n                    source.playbackRate.value = speed;\r\n                    source.start(0, start, end - start);\r\n                    \r\n                    let currentNode = source;\r\n                    if(self.UI.renderBar) self.UI.renderBar.style.width = '30%';\r\n                    \r\n                    if (self.UI.sliderVol) {\r\n                        const gainNode = offlineCtx.createGain();\r\n                        gainNode.gain.value = parseFloat(self.UI.sliderVol.value);\r\n                        currentNode.connect(gainNode);\r\n                        currentNode = gainNode;\r\n                    }\r\n\r\n                    if (self.UI.togNoise && self.UI.togNoise.checked) {\r\n                        const hp = offlineCtx.createBiquadFilter(); hp.type = 'highpass'; hp.frequency.value = 120;\r\n                        const lp = offlineCtx.createBiquadFilter(); lp.type = 'lowpass'; lp.frequency.value = 9000;\r\n                        currentNode.connect(hp); hp.connect(lp); currentNode = lp;\r\n                    }\r\n                    if (self.UI.togAcapella && self.UI.togAcapella.checked) {\r\n                        const bp = offlineCtx.createBiquadFilter(); bp.type = 'bandpass'; bp.frequency.value = 1200; bp.Q.value = 0.8;\r\n                        currentNode.connect(bp); currentNode = bp;\r\n                    }\r\n                    if (self.UI.togKaraoke && self.UI.togKaraoke.checked && channels === 2) {\r\n                        const splitter = offlineCtx.createChannelSplitter(2);\r\n                        const inverter = offlineCtx.createGain(); inverter.gain.value = -1;\r\n                        const mixer = offlineCtx.createGain();\r\n                        currentNode.connect(splitter); splitter.connect(inverter, 1);\r\n                        splitter.connect(mixer, 0); inverter.connect(mixer, 0);\r\n                        currentNode = mixer;\r\n                    }\r\n\r\n                    if(self.UI.eqBass) {\r\n                        const bassFilter = offlineCtx.createBiquadFilter(); bassFilter.type = 'lowshelf'; bassFilter.frequency.value = 300; bassFilter.gain.value = parseFloat(self.UI.eqBass.value);\r\n                        const midFilter = offlineCtx.createBiquadFilter(); midFilter.type = 'peaking'; midFilter.frequency.value = 1500; midFilter.Q.value = 1; midFilter.gain.value = parseFloat(self.UI.eqMid.value);\r\n                        const trebleFilter = offlineCtx.createBiquadFilter(); trebleFilter.type = 'highshelf'; trebleFilter.frequency.value = 4000; trebleFilter.gain.value = parseFloat(self.UI.eqTreble.value);\r\n                        currentNode.connect(bassFilter); bassFilter.connect(midFilter); midFilter.connect(trebleFilter); currentNode = trebleFilter;\r\n                    }\r\n\r\n                    if (self.UI.togCompressor && self.UI.togCompressor.checked) {\r\n                        const compressor = offlineCtx.createDynamicsCompressor();\r\n                        compressor.threshold.value = -24; compressor.knee.value = 12; compressor.ratio.value = 8;\r\n                        compressor.attack.value = 0.003; compressor.release.value = 0.25;\r\n                        currentNode.connect(compressor); currentNode = compressor;\r\n                    }\r\n                    \r\n                    if(self.UI.renderBar) self.UI.renderBar.style.width = '70%';\r\n                    currentNode.connect(offlineCtx.destination);\r\n                    \r\n                    const renderedBuffer = await offlineCtx.startRendering();\r\n                    if(self.UI.renderBar) self.UI.renderBar.style.width = '85%';\r\n\r\n                    \/\/ \u23ea Reverse: flip the final rendered samples in each channel\r\n                    if (self.UI.togReverse && self.UI.togReverse.checked) {\r\n                        for (let ch = 0; ch < renderedBuffer.numberOfChannels; ch++) {\r\n                            renderedBuffer.getChannelData(ch).reverse();\r\n                        }\r\n                    }\r\n                    \r\n                    const format = self.UI.exportFormat ? self.UI.exportFormat.value : 'mp3';\r\n                    \r\n                    let finalBlob;\r\n                    if (format === 'mp3') {\r\n                        finalBlob = self.bufferToMp3Blob(renderedBuffer);\r\n                    } else if (format === 'wav') {\r\n                        finalBlob = self.bufferToWavBlob(renderedBuffer);\r\n                    } else {\r\n                        const wavBlob = self.bufferToWavBlob(renderedBuffer);\r\n                        finalBlob = new Blob([wavBlob], { type: 'audio\/' + format });\r\n                    }\r\n                    \r\n                    if(self.UI.renderBar) self.UI.renderBar.style.width = '100%';\r\n\r\n                    const url = URL.createObjectURL(finalBlob);\r\n                    const a = document.createElement(\"a\"); a.style.display = \"none\"; a.href = url; a.download = 'PrimeToolHub_Master.' + format;\r\n                    document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a);\r\n                    self.showToast('Downloaded Successfully!');\r\n                    \r\n                } catch(err) {\r\n                    console.error(err);\r\n                    self.showToast('Error processing audio. File might be too large.', true);\r\n                } finally {\r\n                    if(self.UI.renderOverlay) self.UI.renderOverlay.style.display = 'none';\r\n                    if(self.UI.processBtn) self.UI.processBtn.disabled = false;\r\n                }\r\n            }, 100);\r\n        },\r\n\r\n        resetStudio: function() {\r\n            if(this.wavesurfer) { this.wavesurfer.destroy(); this.wavesurfer = null; }\r\n            this.originalBuffer = null; this.originalFile = null;\r\n            if(this.UI.fileIn) this.UI.fileIn.value = '';\r\n            if(this.UI.uploadArea) this.UI.uploadArea.style.display = 'block';\r\n            if(this.UI.workspace) this.UI.workspace.style.display = 'none';\r\n            if(this.UI.closeBtn) this.UI.closeBtn.style.display = 'none';\r\n            if(this.UI.videoPreview) { this.UI.videoPreview.style.display = 'none'; this.UI.videoPreview.src = ''; }\r\n            if(this.UI.togNoise) this.UI.togNoise.checked = false; \r\n            if(this.UI.togAcapella) this.UI.togAcapella.checked = false; \r\n            if(this.UI.togKaraoke) this.UI.togKaraoke.checked = false;\r\n            if(this.UI.togReverse) this.UI.togReverse.checked = false;\r\n            this.stopRecording();\r\n        },\r\n\r\n        \/\/ --- MIC RECORDING ---\r\n        startRecording: async function() {\r\n            try {\r\n                const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\r\n                this.mediaRecorder = new MediaRecorder(stream);\r\n                this.audioChunks = [];\r\n                this.recSeconds = 0;\r\n                \r\n                const updateTimer = () => {\r\n                    this.recSeconds++;\r\n                    const formatted = this.formatTime(this.recSeconds);\r\n                    if(this.UI.recTime) this.UI.recTime.textContent = formatted;\r\n                    if(this.UI.wsRecTime) this.UI.wsRecTime.textContent = formatted;\r\n                };\r\n\r\n                this.recordingTimer = setInterval(updateTimer, 1000);\r\n                \r\n                this.mediaRecorder.ondataavailable = e => { if(e.data.size > 0) this.audioChunks.push(e.data); };\r\n                this.mediaRecorder.onstop = () => {\r\n                    const audioBlob = new Blob(this.audioChunks, { type: 'audio\/webm' });\r\n                    this.loadAudioFile(audioBlob, 'Microphone_Recording.webm');\r\n                    \r\n                    if(this.UI.recordingStatus) this.UI.recordingStatus.style.display = 'none';\r\n                    if(this.UI.wsRecordingStatus) this.UI.wsRecordingStatus.style.display = 'none';\r\n                    if(this.UI.recordBtn) this.UI.recordBtn.style.display = 'inline-flex';\r\n                    if(this.UI.wsRecordBtn) this.UI.wsRecordBtn.style.display = 'inline-flex';\r\n                    if(this.UI.stopRecordBtn) this.UI.stopRecordBtn.style.display = 'none';\r\n                    if(this.UI.wsStopRecordBtn) this.UI.wsStopRecordBtn.style.display = 'none';\r\n                    \r\n                    clearInterval(this.recordingTimer);\r\n                };\r\n                \r\n                this.mediaRecorder.start();\r\n                if(this.UI.recordingStatus) this.UI.recordingStatus.style.display = 'block';\r\n                if(this.UI.wsRecordingStatus) this.UI.wsRecordingStatus.style.display = 'block';\r\n                if(this.UI.recordBtn) this.UI.recordBtn.style.display = 'none';\r\n                if(this.UI.wsRecordBtn) this.UI.wsRecordBtn.style.display = 'none';\r\n                if(this.UI.stopRecordBtn) this.UI.stopRecordBtn.style.display = 'inline-flex';\r\n                if(this.UI.wsStopRecordBtn) this.UI.wsStopRecordBtn.style.display = 'inline-flex';\r\n                \r\n                this.showToast('Recording started... Speak now!');\r\n            } catch(err) { this.showToast('Microphone access denied.', true); }\r\n        },\r\n        stopRecording: function() {\r\n            if (this.mediaRecorder && this.mediaRecorder.state === 'recording') {\r\n                this.mediaRecorder.stop();\r\n                this.mediaRecorder.stream.getTracks().forEach(track => track.stop());\r\n            }\r\n        },\r\n\r\n        \/\/ --- JOINER FUNCTIONS (WITH PRE-TRIM) ---\r\n        addJoinerFiles: async function(files) {\r\n            if(this.UI.joinBtn) { this.UI.joinBtn.innerText = \"\u23f3 Loading Files...\"; this.UI.joinBtn.disabled = true; }\r\n            \r\n            const AudioContext = window.AudioContext || window.webkitAudioContext;\r\n            const audioCtx = new AudioContext();\r\n\r\n            for(let file of files) {\r\n                if (file.type.startsWith('audio\/') || file.type.startsWith('video\/') || file.name.match(\/\\.(mp3|wav|ogg|m4a|mp4)$\/i)) {\r\n                    try {\r\n                        const arrayBuffer = await file.arrayBuffer();\r\n                        const buffer = await new Promise((resolve, reject) => audioCtx.decodeAudioData(arrayBuffer, resolve, reject));\r\n                        this.joinerFiles.push({\r\n                            id: Math.random().toString(36).substring(7),\r\n                            file: file,\r\n                            name: file.name,\r\n                            buffer: buffer,\r\n                            start: 0,\r\n                            end: buffer.duration\r\n                        });\r\n                    } catch(e) {\r\n                        console.error(\"Error decoding\", file.name);\r\n                    }\r\n                }\r\n            }\r\n            this.renderJoinerList();\r\n            if(this.UI.joinBtn) { this.UI.joinBtn.innerText = \"\ud83d\udd17 Merge Audio Files\"; this.UI.joinBtn.disabled = false; }\r\n        },\r\n        renderJoinerList: function() {\r\n            if(!this.UI.joinList) return;\r\n            this.UI.joinList.innerHTML = '';\r\n            if(this.joinerFiles.length > 0) {\r\n                this.UI.joinList.style.display = 'block';\r\n                if(this.UI.joinActions) this.UI.joinActions.style.display = 'block';\r\n                \r\n                const self = this;\r\n                this.joinerFiles.forEach((fileObj, index) => {\r\n                    const isTrimmed = fileObj.start > 0 || fileObj.end < fileObj.buffer.duration;\r\n                    const trimText = isTrimmed ? `<span style=\"font-size:0.75rem; background:#fee2e2; color:#ef4444; padding:2px 6px; border-radius:4px; margin-left:8px;\">\u2702\ufe0f ${self.formatTime(fileObj.start)} - ${self.formatTime(fileObj.end)}<\/span>` : '';\r\n                    \r\n                    this.UI.joinList.innerHTML += `\r\n                        <li class=\"pth-joiner-item\" style=\"border:1px solid #e2e8f0; margin-bottom:8px; padding:10px 15px; border-radius:8px; background:#fff; display:flex; justify-content:space-between; align-items:center;\">\r\n                            <span>\ud83c\udfb5 ${fileObj.name} ${trimText}<\/span>\r\n                            <div class=\"pth-joiner-actions-btn\">\r\n                                <button class=\"pth-joiner-edit\" onclick=\"window.pthAudioMultiToolApp.openJoinerEditor(${index})\">\u2702\ufe0f Edit\/Trim<\/button>\r\n                                <button class=\"pth-joiner-del\" onclick=\"window.pthAudioMultiToolApp.removeJoinerFile(${index})\">\u2716<\/button>\r\n                            <\/div>\r\n                        <\/li>`;\r\n                });\r\n            } else {\r\n                this.UI.joinList.style.display = 'none';\r\n                if(this.UI.joinActions) this.UI.joinActions.style.display = 'none';\r\n                if(this.UI.joinPlayerWrap) this.UI.joinPlayerWrap.style.display = 'none';\r\n            }\r\n        },\r\n        removeJoinerFile: function(index) {\r\n            this.joinerFiles.splice(index, 1);\r\n            this.renderJoinerList();\r\n        },\r\n        \r\n        \/\/ --- JOINER EDITOR FUNCTIONS ---\r\n        openJoinerEditor: function(index) {\r\n            this.currentJoinerEditIndex = index;\r\n            const fileObj = this.joinerFiles[index];\r\n\r\n            if(this.UI.joinMainView) this.UI.joinMainView.style.display = 'none';\r\n            if(this.UI.joinEditorView) this.UI.joinEditorView.style.display = 'block';\r\n            if(this.UI.joinEditName) this.UI.joinEditName.innerText = fileObj.name;\r\n\r\n            if(this.joinerWavesurfer) this.joinerWavesurfer.destroy();\r\n            if(this.UI.joinTimeline) this.UI.joinTimeline.innerHTML = '';\r\n\r\n            this.joinerWavesurfer = WaveSurfer.create({\r\n                container: '#pth-joiner-waveform',\r\n                waveColor: '#fb7185',\r\n                progressColor: '#e11d48',\r\n                cursorColor: '#ffffff',\r\n                barWidth: 2, barGap: 1, barRadius: 2, height: 100, normalize: true,\r\n                plugins: [\r\n                    window.WaveSurfer.Timeline.create({\r\n                        container: '#pth-joiner-timeline',\r\n                        height: 20, timeInterval: 0.1, primaryLabelInterval: 1,\r\n                        style: { fontSize: '11px', color: '#94a3b8' }\r\n                    })\r\n                ]\r\n            });\r\n\r\n            if (window.WaveSurfer.Regions) {\r\n                this.joinerWsRegions = this.joinerWavesurfer.registerPlugin(window.WaveSurfer.Regions.create());\r\n                this.joinerWsRegions.enableDragSelection({ color: 'rgba(244, 63, 94, 0.4)' });\r\n            }\r\n\r\n            const blob = this.bufferToWavBlob(fileObj.buffer);\r\n            this.joinerWavesurfer.load(URL.createObjectURL(blob));\r\n\r\n            this.joinerWavesurfer.on('ready', () => {\r\n                if(this.joinerWsRegions && (fileObj.start > 0 || fileObj.end < fileObj.buffer.duration)) {\r\n                    this.joinerWsRegions.addRegion({\r\n                        start: fileObj.start,\r\n                        end: fileObj.end,\r\n                        color: 'rgba(244, 63, 94, 0.4)'\r\n                    });\r\n                }\r\n            });\r\n\r\n            const self = this;\r\n            this.joinerWavesurfer.on('play', () => { \r\n                if(self.UI.joinIconPlay) self.UI.joinIconPlay.style.display = 'none'; \r\n                if(self.UI.joinIconPause) self.UI.joinIconPause.style.display = 'block'; \r\n            });\r\n            this.joinerWavesurfer.on('pause', () => { \r\n                if(self.UI.joinIconPlay) self.UI.joinIconPlay.style.display = 'block'; \r\n                if(self.UI.joinIconPause) self.UI.joinIconPause.style.display = 'none'; \r\n            });\r\n        },\r\n        saveJoinerTrim: function() {\r\n            if(!this.joinerWsRegions) return this.closeJoinerEditor();\r\n            const regions = this.joinerWsRegions.getRegions();\r\n            const fileObj = this.joinerFiles[this.currentJoinerEditIndex];\r\n            \r\n            if(regions.length > 0) {\r\n                fileObj.start = regions[0].start;\r\n                fileObj.end = regions[0].end;\r\n                this.showToast('Trim saved for ' + fileObj.name);\r\n            } else {\r\n                fileObj.start = 0;\r\n                fileObj.end = fileObj.buffer.duration;\r\n                this.showToast('Full length restored for ' + fileObj.name);\r\n            }\r\n            this.closeJoinerEditor();\r\n        },\r\n        closeJoinerEditor: function() {\r\n            if(this.joinerWavesurfer) { this.joinerWavesurfer.destroy(); this.joinerWavesurfer = null; }\r\n            if(this.UI.joinEditorView) this.UI.joinEditorView.style.display = 'none';\r\n            if(this.UI.joinMainView) this.UI.joinMainView.style.display = 'block';\r\n            this.renderJoinerList();\r\n        },\r\n\r\n        joinAudioFiles: async function() {\r\n            if(this.joinerFiles.length < 2) return this.showToast('Add at least 2 files to join.', true);\r\n            if(this.UI.joinBtn) { this.UI.joinBtn.innerText = \"\u23f3 Merging Audio...\"; this.UI.joinBtn.disabled = true; }\r\n            \r\n            try {\r\n                let totalLength = 0;\r\n                const sampleRate = this.joinerFiles[0].buffer.sampleRate;\r\n                const channels = this.joinerFiles[0].buffer.numberOfChannels;\r\n                \r\n                for(let fileObj of this.joinerFiles) {\r\n                    totalLength += (fileObj.end - fileObj.start) * sampleRate;\r\n                }\r\n                \r\n                const OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;\r\n                const offlineCtx = new OfflineAudioContext(channels, totalLength, sampleRate);\r\n                \r\n                let offset = 0;\r\n                for(let fileObj of this.joinerFiles) {\r\n                    const source = offlineCtx.createBufferSource();\r\n                    source.buffer = fileObj.buffer;\r\n                    source.connect(offlineCtx.destination);\r\n                    const duration = fileObj.end - fileObj.start;\r\n                    source.start(offset, fileObj.start, duration);\r\n                    offset += duration;\r\n                }\r\n                \r\n                const renderedBuffer = await offlineCtx.startRendering();\r\n                const format = this.UI.joinFormat ? this.UI.joinFormat.value : 'mp3';\r\n                \r\n                let finalBlob;\r\n                if (format === 'mp3') {\r\n                    finalBlob = this.bufferToMp3Blob(renderedBuffer);\r\n                } else {\r\n                    finalBlob = this.bufferToWavBlob(renderedBuffer);\r\n                }\r\n\r\n                this.joinedBlob = finalBlob;\r\n                const url = URL.createObjectURL(finalBlob);\r\n                \r\n                if(this.UI.joinPlayerWrap && this.UI.joinPlayer) {\r\n                    this.UI.joinPlayerWrap.style.display = 'block';\r\n                    this.UI.joinPlayer.src = url;\r\n                }\r\n                \r\n                if(this.UI.joinDlBtn) {\r\n                    this.UI.joinDlBtn.style.display = 'block';\r\n                    this.UI.joinBtn.style.display = 'none';\r\n                }\r\n\r\n                this.showToast('Files merged successfully! Preview or download.');\r\n            } catch(e) {\r\n                console.error(e);\r\n                this.showToast('Error joining files.', true);\r\n            } finally {\r\n                if(this.UI.joinBtn) { this.UI.joinBtn.innerText = \"\ud83d\udd17 Merge Audio Files\"; this.UI.joinBtn.disabled = false; }\r\n            }\r\n        },\r\n        downloadJoinedFile: function() {\r\n            if(!this.joinedBlob) return;\r\n            const format = this.UI.joinFormat ? this.UI.joinFormat.value : 'mp3';\r\n            const url = URL.createObjectURL(this.joinedBlob);\r\n            const a = document.createElement(\"a\"); a.style.display = \"none\"; a.href = url; a.download = 'Joined_Audio.' + format;\r\n            document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url);\r\n            this.showToast('Downloaded Successfully!');\r\n        },\r\n\r\n        \/\/ --- CONVERTER FUNCTIONS ---\r\n        convertAudio: async function() {\r\n            if(!this.UI.convFile) return;\r\n            const file = this.UI.convFile.files[0];\r\n            if(!file) return this.showToast('Please select a file to convert.', true);\r\n            \r\n            if(this.UI.convertBtn) { this.UI.convertBtn.innerText = \"\u23f3 Converting...\"; this.UI.convertBtn.disabled = true; }\r\n            \r\n            try {\r\n                const AudioContext = window.AudioContext || window.webkitAudioContext;\r\n                const audioCtx = new AudioContext();\r\n                const arrayBuffer = await file.arrayBuffer();\r\n                const buffer = await new Promise((resolve, reject) => audioCtx.decodeAudioData(arrayBuffer, resolve, reject));\r\n                \r\n                const format = this.UI.convTarget ? this.UI.convTarget.value : 'mp3';\r\n                let finalBlob;\r\n\r\n                if(format === 'mp3') {\r\n                    finalBlob = this.bufferToMp3Blob(buffer);\r\n                } else {\r\n                    const wavBlob = this.bufferToWavBlob(buffer);\r\n                    finalBlob = format === 'wav' ? wavBlob : new Blob([wavBlob], { type: 'audio\/' + format });\r\n                }\r\n                \r\n                const url = URL.createObjectURL(finalBlob);\r\n                const a = document.createElement(\"a\"); a.style.display = \"none\"; a.href = url; a.download = 'Converted_Audio.' + format;\r\n                document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url);\r\n                this.showToast('Conversion successful!');\r\n            } catch(e) {\r\n                this.showToast('Conversion failed. Video\/Audio might be corrupt.', true);\r\n            } finally {\r\n                if(this.UI.convertBtn) { this.UI.convertBtn.innerText = \"\ud83d\udd04 Convert Now\"; this.UI.convertBtn.disabled = false; }\r\n            }\r\n        }\r\n    };\r\n\r\n    function runInit() {\r\n        if (!window.pthAudioMultiToolApp.initialized && document.getElementById('pth-audio-studio-wrapper')) {\r\n            window.pthAudioMultiToolApp.initialized = true;\r\n            window.pthAudioMultiToolApp.init();\r\n        }\r\n    }\r\n    if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(runInit, 1); }\r\n    else { document.addEventListener('DOMContentLoaded', runInit); window.addEventListener('load', runInit); }\r\n})();\r\n<\/script>\r\n\r\n<script data-no-optimize=\"1\" data-no-minify=\"1\" data-cfasync=\"false\">\r\n(function () {\r\n  'use strict';\r\n  function mount() {\r\n    var panel = document.getElementById('pth-as-tab-voice');\r\n    var tool  = document.getElementById('pth-audio-studio-wrapper');\r\n    if (!panel || !tool) return false;\r\n    if (tool.parentNode !== panel) {\r\n      var ph = document.getElementById('pth-as-empty-voice');\r\n      if (ph && ph.parentNode) ph.parentNode.removeChild(ph);\r\n      tool.style.margin = '0';\r\n      panel.appendChild(tool);\r\n    }\r\n    return true;\r\n  }\r\n  if (mount()) return;\r\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);\r\n  window.addEventListener('load', function () { setTimeout(mount, 60); });\r\n  if (typeof MutationObserver !== 'undefined') {\r\n    var o = new MutationObserver(function () { if (mount()) o.disconnect(); });\r\n    o.observe(document.body || document.documentElement, { childList: true, subtree: true });\r\n    setTimeout(function () { o.disconnect(); }, 10000);\r\n  }\r\n})();\r\n<\/script>\n\n\n\n<div id=\"start-seo-article\"><\/div>\n\n\n\n<style>\n.pth-feature-wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px;margin-bottom:30px}\n.pth-feat-box{border:1px solid #e2e8f0;background:#fff;padding:24px;border-radius:12px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.02);transition:all .3s ease}\n.pth-feat-box:hover{border-color:#bfdbfe;transform:translateY(-3px);box-shadow:0 10px 15px -3px rgba(0,0,0,0.05)}\n.pth-feat-title{font-size:1.05rem;font-weight:800;color:#0f172a;margin-bottom:10px;display:flex;align-items:center;gap:10px}\n.pth-feat-text{font-size:.9rem;color:#1e293b;line-height:1.6;margin:0;font-weight:500}\n.pth-steps-wrap{border:1px solid #e2e8f0;background:#fff;padding:40px 30px;border-radius:16px;text-align:center;box-shadow:0 4px 6px -1px rgba(0,0,0,0.02);margin-bottom:40px}\n.pth-steps-title{font-size:1.2rem;font-weight:800;color:#0f172a;text-transform:uppercase;margin-bottom:35px;letter-spacing:1px}\n.pth-steps-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:30px}\n.pth-step-item{display:flex;flex-direction:column;align-items:center}\n.pth-step-num{width:45px;height:45px;background:#7c3aed;color:#fff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:1.2rem;margin-bottom:15px;box-shadow:0 4px 10px rgba(124,58,237,.3)}\n.pth-step-name{font-weight:800;color:#0f172a;margin-bottom:8px;font-size:1rem}\n.pth-step-desc{font-size:.9rem;color:#1e293b;line-height:1.5;margin:0;font-weight:500}\n<\/style>\n\n<div class=\"pth-feature-wrap\">\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udfe2 16 Audio Tools in One Engine<\/div>\n        <p class=\"pth-feat-text\">Convert to MP3\/WAV\/OGG, Trim by MM:SS, adjust Volume up to 300%, shift Pitch, change Speed, apply Bass Boost, Normalize to -16 LUFS, add Echo, Reverse, Fade In\/Out, Robot Voice, Chipmunk, Cut Silence, 8D Audio, and Tremolo \u2014 all inside a single FFmpeg WASM engine that never touches a server.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udd35 Self-Loading Engine with Progress Bar<\/div>\n        <p class=\"pth-feat-text\">No header snippet required. The tool dynamically injects the FFmpeg library script itself, then shows a staged loading progress bar \u2014 from &#8220;Fetching WASM core&#8221; through &#8220;Compiling WebAssembly modules&#8221; to &#8220;Engine Ready&#8221;. Users see exactly what is happening during the ~30MB one-time download rather than a frozen &#8220;Please Wait&#8221; state.<\/p>\n    <\/div>\n    <div class=\"pth-feat-box\">\n        <div class=\"pth-feat-title\">\ud83d\udfe3 Output Format Selector + File Info Panel<\/div>\n        <p class=\"pth-feat-text\">Choose MP3, WAV, or OGG as your output before hitting \ud83d\ude80 PROCESS AUDIO. The sidebar File Info panel shows the loaded file&#8217;s name, detected duration, input size, currently active tool, and chosen output format \u2014 updating live as you switch tools and formats without reprocessing.<\/p>\n    <\/div>\n<\/div>\n\n<div class=\"pth-steps-wrap\">\n    <div class=\"pth-steps-title\">How to Use Audio Studio Pro<\/div>\n    <div class=\"pth-steps-grid\">\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">1<\/div>\n            <div class=\"pth-step-name\">Wait for Engine<\/div>\n            <p class=\"pth-step-desc\">Watch the purple loading bar fill from 0% to 100%. When the badge changes to &#8220;\u2705 Engine Ready&#8221; and the upload button unlocks to &#8220;\ud83d\udcc1 Select Audio \/ Video&#8221;, the FFmpeg WASM engine is fully compiled and ready.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">2<\/div>\n            <div class=\"pth-step-name\">Upload Your File<\/div>\n            <p class=\"pth-step-desc\">Click &#8220;\ud83d\udcc1 Select Audio \/ Video&#8221; and pick any MP3, WAV, M4A, OGG, FLAC, or MP4 file. The Original audio player loads your file immediately and the File Info sidebar shows name, duration, and file size. Use &#8220;\ud83d\udca1 Load Sample&#8221; to test with a generated 440Hz tone.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">3<\/div>\n            <div class=\"pth-step-name\">Pick Tool + Settings<\/div>\n            <p class=\"pth-step-desc\">Search tools by name using the search box (e.g. type &#8220;echo&#8221; or &#8220;pitch&#8221;) or click any of the 16 tool buttons. Adjust the settings that appear \u2014 bitrate selector for Convert, MM:SS inputs for Trim, range sliders for Volume\/Speed\/Pitch. Then pick Output Format: MP3, WAV, or OGG.<\/p>\n        <\/div>\n        <div class=\"pth-step-item\">\n            <div class=\"pth-step-num\">4<\/div>\n            <div class=\"pth-step-name\">Process + Download<\/div>\n            <p class=\"pth-step-desc\">Click &#8220;\ud83d\ude80 PROCESS AUDIO&#8221;. Watch the progress bar and processing log update in real time. When complete, the Output player loads the result automatically and the green &#8220;\u2b07\ufe0f Download&#8221; button appears with the file size. Use &#8220;\ud83d\udd04 Reset Studio&#8221; to start fresh for your next track.<\/p>\n        <\/div>\n    <\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Last updated: June 2026<\/p>\n\n\n\n<h2 id=\"\ud83d\udd34-how-f-fmpeg-wasm-runs-audio-processing-inside-your-browser\" class=\"wp-block-heading\">\ud83d\udd34 <strong>How FFmpeg WASM Runs Audio Processing Inside Your Browser<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most online audio tools send your file to a server. A backend process runs FFmpeg, the output gets stored temporarily, you download it, and the server logs your file. This tool works in the opposite direction. The entire FFmpeg binary \u2014 the same command-line engine used in professional broadcast production \u2014 is compiled to <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/WebAssembly\" rel=\"noreferrer noopener\" target=\"_blank\">WebAssembly (WASM)<\/a> and executed inside your browser tab. No file ever leaves your device. The processing log you see in the dark console panel at the bottom of the tool shows the exact FFmpeg commands running in real time \u2014 not simulated output, actual engine messages.<\/p>\n\n\n<figure class=\"pth-article-figure pth-img-right\" style=\"float:right; width:700px; max-width:100%; margin:4px 0 16px 28px; clear:right;\"><img decoding=\"async\" src=\"https:\/\/schoolict.net\/tools\/wp-content\/uploads\/2026\/06\/FFmpeg-WASM-audio-processing-flow-diagram-\u2014-browser-audio-editor-converting-MP3-WAV-OGG-with-WebAssembly-engine-800x447.jpeg\" alt=\"FFmpeg WASM audio processing flow diagram \u2014 browser audio editor converting MP3 WAV OGG with WebAssembly engine\" width=\"700\" height=\"394\" loading=\"lazy\" data-no-lazy=\"1\" class=\"pth-article-img\" style=\"width:100%;height:auto;display:block;border-radius:10px;border:1px solid #e2e8f0;\"><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The engine uses a single-thread safe build of FFmpeg core (<code>@ffmpeg\/core-st<\/code>). This specific build avoids <code>SharedArrayBuffer<\/code> \u2014 a browser API that requires special COOP\/COEP HTTP headers which most shared WordPress hosting setups don&#8217;t send. The single-thread build sacrifices parallel processing speed for universal compatibility. On a typical 5MB MP3 file, operations like Normalize or Bass Boost complete in 8\u201320 seconds depending on your device&#8217;s CPU speed. Trim with stream copy (<code>-c copy<\/code>) is faster since it skips re-encoding entirely.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe1-what-each-audio-tool-does-the-f-fmpeg-filter-behind-every-button\" class=\"wp-block-heading\">\ud83d\udfe1 <strong>What Each Audio Tool Does \u2014 The FFmpeg Filter Behind Every Button<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every tool in the grid maps to a specific FFmpeg audio filter or operation. Understanding what runs under the hood helps you chain tools effectively and predict the output quality.<\/p>\n\n\n\n<h3 id=\"\ud83d\udfe2-convert-trim-and-volume-the-foundation-tools\" class=\"wp-block-heading\">\ud83d\udfe2 <strong>Convert, Trim, and Volume \u2014 The Foundation Tools<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Convert to MP3\/WAV\/OGG<\/strong> re-encodes your source file at the bitrate you select \u2014 320kbps for archival quality, 128kbps when file size matters. WAV output uses <code>pcm_s16le<\/code> (uncompressed 16-bit linear PCM), which is what audio editors like Audacity and DaVinci Resolve import without issues. OGG uses <code>libvorbis -q:a 4<\/code>, producing roughly 128kbps variable-rate output.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Trim<\/strong> takes Start and End times in MM:SS format. The tool auto-fills the End Time field with your file&#8217;s exact duration when the audio loads, so you don&#8217;t need to know it in advance. FFmpeg runs <code>-ss [start] -t [duration]<\/code> with a <code>-vn<\/code> flag to strip any video track. If you&#8217;re trimming a long MP4 to extract a short audio segment, this is the fastest path \u2014 the seek is frame-accurate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Volume<\/strong> uses <code>volume=[factor]<\/code> where the slider range of 0\u2013300% maps to 0.0\u20133.0. Setting 200% doubles the amplitude \u2014 equivalent to +6dB. Going beyond 200% risks clipping on tracks that are already loud. Run Normalize first, then boost Volume if you need more output level without distortion.<\/p>\n\n\n\n<h3 id=\"\ud83d\udfe2-effects-tools-normalize-eq-spatial-and-voice-changers\" class=\"wp-block-heading\">\ud83d\udfe2 <strong>Effects Tools \u2014 Normalize, EQ, Spatial, and Voice Changers<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Normalize<\/strong> runs <code>loudnorm=I=-16:TP=-1.5:LRA=11<\/code> \u2014 the EBU R128 loudness standard used by Spotify, YouTube, and broadcast television. It brings integrated loudness to -16 LUFS without clipping. This is the single most useful tool for podcast cleanup, YouTube uploads, and making multiple audio clips sound consistent next to each other. Run it as the final step before download.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Bass Boost<\/strong> uses <code>bass=g=10:f=110:w=0.6<\/code> \u2014 a shelving EQ that boosts frequencies around 110Hz by 10dB with a moderate Q. This targets the sub-bass and bass range without muddying the mid-range where vocals sit. <strong>8D Audio<\/strong> applies <code>apulsator=hz=0.125<\/code>, a stereo panning oscillator that sweeps the stereo field at 0.125Hz (one full cycle every 8 seconds). It needs stereo headphones to perceive \u2014 mono speaker output collapses the effect entirely. <strong>Cut Silence<\/strong> uses <code>silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-30dB<\/code>, which strips any gap longer than 1 second that falls below -30dB. Perfect for recorded interviews, online meetings, and podcast raw recordings where dead air between speakers adds unnecessary length.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <strong>Pitch Shift<\/strong> filter uses <code>asetrate=44100*[factor],aresample=44100<\/code>. This changes the sample rate to trick playback speed, then resamples back to 44100Hz. A factor of 1.5 raises pitch approximately a musical fifth. Unlike a true pitch-shifting algorithm (which preserves tempo), this method also speeds up the audio \u2014 the same trade-off used by the Chipmunk and Robot Voice effects. For tempo-independent pitch shifting you&#8217;d need a WSOLA or phase vocoder algorithm not available in this FFmpeg build. Our <a href=\"\/free-all-in-one-audio-editor-converter\/\">Audio Studio Pro tool<\/a> applies these filters in sequence \u2014 so if you need to normalize AND add echo, process the file twice: normalize first, then re-upload and apply echo.<\/p>\n\n\n\n<h2 id=\"\ud83d\udfe1-choosing-the-right-output-format-mp-3-wav-or-ogg\" class=\"wp-block-heading\">\ud83d\udfe1 <strong>Choosing the Right Output Format \u2014 MP3, WAV, or OGG<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The Output Format selector at the bottom of the settings area changes the FFmpeg output container and codec for every tool, not just Convert. If you apply Echo and select WAV, you get an uncompressed echo-processed audio file. If you select OGG, you get a Vorbis-encoded version. This matters depending on where the output goes next.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\udd35 <strong>MP3<\/strong> \u2014 Best default for sharing, uploading to YouTube\/SoundCloud\/Spotify, or sending to someone without specifying a player. Lossy compression. At 192kbps, quality loss is inaudible on most consumer speakers and headphones.<\/li>\n\n\n\n<li>\ud83d\udfe0 <strong>WAV<\/strong> \u2014 Choose this when the output goes into a DAW (Audacity, GarageBand, Reaper, DaVinci Resolve). Uncompressed PCM means no generation loss when you re-process. File sizes are large \u2014 a 3-minute track at 44.1kHz 16-bit stereo is ~30MB.<\/li>\n\n\n\n<li>\ud83d\udfe3 <strong>OGG Vorbis<\/strong> \u2014 Open-source lossy format with slightly better quality than MP3 at the same bitrate. Supported natively in Chrome and Firefox. Good for game audio assets, web apps, and projects that avoid MP3 licensing concerns. Not supported in Safari without a polyfill.<\/li>\n<\/ul>\n\n\n\n<h3 id=\"\ud83d\udfe2-tool-search-load-sample-and-reset-studio\" class=\"wp-block-heading\">\ud83d\udfe2 <strong>Tool Search, Load Sample, and Reset Studio<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The search box above the tool grid filters all 16 tools by name in real time as you type. Type &#8220;fade&#8221; to show Fade In and Fade Out. Type &#8220;voice&#8221; to show Robot Voice. This matters on mobile where the full grid scrolls off screen. The <strong>\ud83d\udca1 Load Sample<\/strong> button generates a 2-second 440Hz sine wave tone using the Web Audio API \u2014 a pure tone with no harmonics, useful for testing effects like Echo, Tremolo, and 8D Audio without uploading a real file. You can read more about browser audio processing techniques in our <a href=\"https:\/\/schoolict.net\/tools\/free-offline-audio-studio-article\/\">screen and audio capture guide<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udd04 Reset Studio<\/strong> clears the loaded file, resets all inputs to defaults, hides the download button, and wipes the processing log \u2014 without reloading the page. The FFmpeg engine stays loaded in memory, so your next processing operation starts immediately. Use this between tracks in a batch session to keep the tool clean. If FFmpeg runs into a corrupted file or a format it can&#8217;t handle, Reset Studio is the correct recovery step \u2014 it resets the <code>isProcessing<\/code> flag that prevents double-processing and gives the engine a clean state for the next run.<\/p>\n\n\n\n<style>\n.pth-faq-section{margin-top:50px;margin-bottom:40px;font-family:inherit}\n.pth-faq-header{font-size:1.8rem;font-weight:800;color:#0f172a;margin-bottom:25px;border-bottom:2px solid #e2e8f0;padding-bottom:10px;display:flex;align-items:center;gap:10px}\n.pth-faq-grid{display:grid;grid-template-columns:100%;gap:20px}\n@media(min-width:768px){.pth-faq-grid{grid-template-columns:repeat(2,1fr)}}\n@media(min-width:1024px){.pth-faq-grid{grid-template-columns:repeat(3,1fr)}}\n.pth-faq-card{background:#f8fafc;padding:24px;border-radius:12px;border:1px solid #e2e8f0;transition:transform .2s ease;break-inside:avoid}\n.pth-faq-card:hover{transform:translateY(-3px);box-shadow:0 4px 12px rgba(0,0,0,.05)}\n.pth-faq-q{color:#0f172a;font-size:1rem;font-weight:700;margin:0 0 12px;line-height:1.4}\n.pth-faq-a{margin:0;font-size:.95rem;color:#1e293b;line-height:1.6;font-weight:500}\n<\/style>\n\n<div class=\"pth-faq-section\">\n    <div class=\"pth-faq-header\">\ud83e\udd14 Frequently Asked Questions<\/div>\n    <div class=\"pth-faq-grid\">\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">Why does the engine take so long to load the first time?<\/p>\n            <p class=\"pth-faq-a\">The tool downloads ~30MB of FFmpeg WebAssembly binary from a CDN on first use. This is a one-time download per browser session \u2014 subsequent uses in the same tab start processing immediately. The purple loading bar shows real-time progress through each stage: library load, WASM fetch, compilation, and engine init.<\/p>\n        <\/div>\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">Does this tool need a separate header snippet installed in WordPress?<\/p>\n            <p class=\"pth-faq-a\">No. The v4.0 tool dynamically injects the FFmpeg library script itself using <code>document.head.appendChild()<\/code>. It no longer depends on a separate header Woody snippet. If you already have the FFmpeg header snippet installed, it still works fine \u2014 the tool detects the existing library and skips the download step.<\/p>\n        <\/div>\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">Can I process the same file through multiple effects in sequence?<\/p>\n            <p class=\"pth-faq-a\">Yes, but not in a single pass with this tool. Download the output from the first operation, then click &#8220;\ud83d\udd04 Reset Studio&#8221;, re-upload the downloaded file, and apply the next effect. The Reset Studio button keeps the FFmpeg engine loaded so there&#8217;s no wait between operations. For a full multi-effect chain, process in this order: Trim \u2192 Normalize \u2192 effects.<\/p>\n        <\/div>\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">What&#8217;s the difference between Speed and Pitch Shift?<\/p>\n            <p class=\"pth-faq-a\">Speed changes playback tempo using <code>atempo<\/code>, which adjusts timing without changing pitch \u2014 a 1.5x speed file plays faster but stays in the same key. Pitch Shift changes the sample rate (<code>asetrate<\/code>), which alters both pitch and speed simultaneously. A 1.5x pitch factor raises the key by roughly a musical fifth but also shortens the track duration.<\/p>\n        <\/div>\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">Why doesn&#8217;t the 8D Audio effect sound spatial on my speakers?<\/p>\n            <p class=\"pth-faq-a\">8D Audio uses a stereo panning oscillator (<code>apulsator<\/code>) that sweeps audio between left and right channels. Mono speakers or phone speakers that play both channels through one driver will collapse this to a flat volume pulse effect. Use stereo headphones \u2014 the spatial effect becomes clearly audible when each ear receives a separate panned signal.<\/p>\n        <\/div>\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">My audio file processes but the output sounds distorted. What went wrong?<\/p>\n            <p class=\"pth-faq-a\">Distortion usually means the output volume is clipping. This happens when you apply Bass Boost or Volume boost to a track that&#8217;s already at maximum level. Fix it by running Normalize first \u2014 it brings the track to -16 LUFS with -1.5dB true peak headroom \u2014 then re-upload and apply your volume or EQ effect on the normalized file.<\/p>\n        <\/div>\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">What file formats can I upload?<\/p>\n            <p class=\"pth-faq-a\">The tool accepts MP3, WAV, M4A, OGG, FLAC, and MP4 video files (it strips the audio track automatically using <code>-vn<\/code>). The browser file picker accepts <code>audio\/*,video\/*<\/code>, so any audio or video format your OS recognizes will appear in the picker. FFmpeg inside the WASM engine supports additional container formats beyond what the picker label lists.<\/p>\n        <\/div>\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">Is WAV output really lossless compared to MP3?<\/p>\n            <p class=\"pth-faq-a\">WAV output from this tool uses PCM encoding \u2014 no lossy compression applied at the output stage. However, if your source file was already an MP3, the original MP3 artifacts are preserved in the WAV. WAV doesn&#8217;t restore information that MP3 encoding already discarded. For true lossless audio, your source needs to be FLAC, WAV, or another uncompressed format.<\/p>\n        <\/div>\n\n        <div class=\"pth-faq-card\">\n            <p class=\"pth-faq-q\">Does resetting the studio clear the downloaded output file?<\/p>\n            <p class=\"pth-faq-a\">Clicking &#8220;\ud83d\udd04 Reset Studio&#8221; hides the download button and clears the output player, but it does not revoke the Blob URL immediately \u2014 the browser holds the processed file in memory until you close or refresh the tab. If you already clicked the download link before resetting, your file is safely saved to disk regardless of what the tool UI shows afterward.<\/p>\n        <\/div>\n\n    <\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Free All in One Audio Editor &amp; Converter Looking for a free online audio editor? Trim, reverse, convert to MP3, and boost volume instantly in your browser with our 100% offline audio studio. \ud83d\udfe2 16 Audio Tools in One Engine Convert to MP3\/WAV\/OGG, Trim by MM:SS, adjust Volume up to 300%, shift Pitch, change Speed, &#8230; <a title=\"Free All in One Audio Editor &amp; Converter\" class=\"read-more\" href=\"https:\/\/schoolict.net\/tools\/free-all-in-one-audio-editor-converter\/\" aria-label=\"Read more about Free All in One Audio Editor &amp; Converter\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":1326,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1323","page","type-page","status-publish","has-post-thumbnail"],"_links":{"self":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/1323","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/comments?post=1323"}],"version-history":[{"count":0,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/pages\/1323\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media\/1326"}],"wp:attachment":[{"href":"https:\/\/schoolict.net\/tools\/wp-json\/wp\/v2\/media?parent=1323"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}