{"id":68,"date":"2026-03-16T05:35:53","date_gmt":"2026-03-16T05:35:53","guid":{"rendered":"https:\/\/tools.sanepo.com\/?post_type=tool&#038;p=68"},"modified":"2026-03-16T05:35:54","modified_gmt":"2026-03-16T05:35:54","slug":"cut-audio-online","status":"publish","type":"tool","link":"https:\/\/tools.sanepo.com\/id\/features\/cut-audio-online\/","title":{"rendered":"Cut Audio Online"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Professional Audio Cutter<\/title>\n    <link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;600;700&#038;display=swap\" rel=\"stylesheet\">\n    <style>\n        \/* CSS Variables Default (Diadaptasi sesuai referensi) *\/\n        #wpnt-audio-cutter {\n            --bg-primary: #ffffff;\n            --bg-secondary: #f8fafc;\n            --border-color: #e2e8f0;\n            --accent-color: #4361ee;\n            --accent-hover: #3a56d4;\n            --glass-bg: rgba(255, 255, 255, 0.85);\n            --text-color: #1e293b; \/* Ditambahkan agar warna teks terlindungi *\/\n            --card-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);\n            --transition: all 0.3s ease;\n            \n            \/* Tool Specific Variables *\/\n            --wpnt-success: #10b981;\n            --wpnt-danger: #ef4444;\n            --wpnt-radius-lg: 16px;\n            --wpnt-radius-md: 12px;\n            --wpnt-radius-sm: 8px;\n            \n            font-family: 'Inter', sans-serif;\n            color: var(--text-color);\n            width: 100%;\n            margin: 30px auto;\n            background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);\n            border: 1px solid var(--border-color);\n            border-radius: var(--wpnt-radius-lg);\n            box-shadow: var(--card-shadow);\n            padding: 32px;\n            box-sizing: border-box;\n            line-height: 1.5;\n            backdrop-filter: blur(10px);\n            -webkit-backdrop-filter: blur(10px);\n        }\n\n        #wpnt-audio-cutter * {\n            box-sizing: border-box;\n            transition: var(--transition);\n        }\n\n        \/* \ud83d\udee1\ufe0f PROTEKSI WARNA TEKS DARI OVERRIDE TEMA WORDPRESS *\/\n        #wpnt-audio-cutter h3, \n        #wpnt-audio-cutter p, \n        #wpnt-audio-cutter label,\n        #wpnt-audio-cutter .wpnt-stat-row span {\n            color: var(--text-color);\n        }\n\n        #wpnt-audio-cutter .wpnt-val {\n            color: var(--accent-color) !important;\n        }\n\n        #wpnt-audio-cutter .wpnt-privacy-badge {\n            display: inline-block; background: rgba(16, 185, 129, 0.1); color: var(--wpnt-success) !important;\n            border: 1px solid rgba(16, 185, 129, 0.2); padding: 6px 12px; border-radius: 20px;\n            font-size: 0.8rem; font-weight: 600; margin-top: 12px;\n        }\n\n        \/* \ud83c\udf19 SMART DARK MODE ADAPTABILITY *\/\n        \/* 1. Sync dengan Tombol Toggle Website (Class Based) *\/\n        html.dark #wpnt-audio-cutter,\n        body.dark #wpnt-audio-cutter,\n        html.dark-theme #wpnt-audio-cutter,\n        body.dark-theme #wpnt-audio-cutter,\n        [data-theme=\"dark\"] #wpnt-audio-cutter,\n        [data-bs-theme=\"dark\"] #wpnt-audio-cutter {\n            --bg-primary: #0f172a;\n            --bg-secondary: #1e293b;\n            --border-color: #334155;\n            --glass-bg: rgba(15, 23, 42, 0.7);\n            --text-color: #f1f5f9;\n        }\n\n        \/* 2. Sync dengan Sistem OS (Batal otomatis jika toggle website diatur ke light mode) *\/\n        @media (prefers-color-scheme: dark) {\n            html:not(.light):not([data-theme=\"light\"]):not([data-bs-theme=\"light\"]) body:not(.light) #wpnt-audio-cutter {\n                --bg-primary: #0f172a;\n                --bg-secondary: #1e293b;\n                --border-color: #334155;\n                --glass-bg: rgba(15, 23, 42, 0.7);\n                --text-color: #f1f5f9;\n            }\n        }\n\n        \/* Header *\/\n        #wpnt-audio-cutter .wpnt-header { text-align: center; margin-bottom: 24px; }\n        #wpnt-audio-cutter h3 { margin: 0 0 8px 0; font-size: 1.75rem; font-weight: 700; letter-spacing: -0.025em; }\n        #wpnt-audio-cutter p { margin: 0; font-size: 0.95rem; opacity: 0.8; }\n\n        \/* Dropzone *\/\n        #wpnt-audio-cutter .wpnt-dropzone {\n            border: 2px dashed rgba(67, 97, 238, 0.4); border-radius: var(--wpnt-radius-lg);\n            padding: 48px 24px; text-align: center; cursor: pointer; background: var(--glass-bg);\n            position: relative; overflow: hidden; box-shadow: var(--card-shadow); margin-bottom: 24px;\n        }\n        #wpnt-audio-cutter .wpnt-dropzone:hover, #wpnt-audio-cutter .wpnt-dropzone.dragover {\n            border-color: var(--accent-color); background: var(--bg-secondary);\n        }\n        #wpnt-audio-cutter .wpnt-dropzone input[type=\"file\"] { display: none; }\n        #wpnt-audio-cutter .wpnt-icon-upload { color: var(--accent-color); margin-bottom: 16px; width: 48px; height: 48px; display: inline-block; font-size: 48px; line-height: 1; }\n        #wpnt-audio-cutter .file-info { font-weight: 600; color: var(--accent-color); margin-top: 10px; word-break: break-all; }\n\n        \/* Settings Grid *\/\n        #wpnt-audio-cutter .wpnt-settings {\n            display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 24px;\n        }\n        #wpnt-audio-cutter .wpnt-setting-group {\n            background: var(--glass-bg); padding: 16px 20px; border-radius: var(--wpnt-radius-md);\n            border: 1px solid var(--border-color); box-shadow: var(--card-shadow);\n        }\n        #wpnt-audio-cutter .wpnt-label {\n            display: flex; justify-content: space-between; align-items: center; font-weight: 600;\n            font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.8; margin-bottom: 12px;\n        }\n        #wpnt-audio-cutter .wpnt-val {\n            font-weight: 700; opacity: 1; width: 100%;\n            background: transparent; border: none; font-size: 1.2rem; outline: none; font-family: inherit;\n        }\n\n        \/* Buttons *\/\n        #wpnt-audio-cutter .wpnt-btn {\n            background: var(--accent-color); color: #ffffff !important; border: none; padding: 12px 24px;\n            border-radius: var(--wpnt-radius-sm); cursor: pointer; font-weight: 600; font-size: 1rem;\n            display: inline-flex; align-items: center; justify-content: center; text-decoration: none; gap: 8px; width: 100%;\n        }\n        #wpnt-audio-cutter .wpnt-btn:hover:not(:disabled) {\n            background: var(--accent-hover); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(67, 97, 238, 0.25);\n        }\n        #wpnt-audio-cutter .wpnt-btn:disabled { opacity: 0.5; cursor: not-allowed; }\n        \n        #wpnt-audio-cutter .wpnt-btn-secondary {\n            background: transparent; color: var(--accent-color) !important; border: 1px solid var(--accent-color);\n        }\n        #wpnt-audio-cutter .wpnt-btn-secondary:hover:not(:disabled) {\n            background: rgba(67, 97, 238, 0.05); color: var(--accent-hover) !important;\n        }\n\n        \/* Progress Bar *\/\n        #wpnt-audio-cutter .progress-container {\n            display: none; margin-top: 24px; background: var(--glass-bg); padding: 20px;\n            border-radius: var(--wpnt-radius-md); border: 1px solid var(--border-color);\n        }\n        #wpnt-audio-cutter .progress-label { font-weight: 600; font-size: 0.9rem; margin-bottom: 8px; text-align: center; }\n        #wpnt-audio-cutter .progress-bar-bg {\n            width: 100%; height: 8px; background: var(--border-color); border-radius: 4px; overflow: hidden; margin-top: 10px;\n        }\n        #wpnt-audio-cutter .progress-bar-fill {\n            height: 100%; width: 0%; background: var(--accent-color); border-radius: 4px; transition: width 0.1s linear;\n        }\n\n        \/* Results Card *\/\n        #wpnt-audio-cutter .wpnt-results { display: none; margin-top: 24px; }\n        #wpnt-audio-cutter .wpnt-card {\n            border: 1px solid var(--border-color); border-radius: var(--wpnt-radius-md); padding: 20px;\n            background: var(--glass-bg); display: flex; flex-direction: column; box-shadow: var(--card-shadow); position: relative;\n        }\n        #wpnt-audio-cutter .wpnt-stats { font-size: 0.9rem; margin-bottom: 16px; }\n        #wpnt-audio-cutter .wpnt-stat-row {\n            display: flex; justify-content: space-between; margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px dashed var(--border-color);\n        }\n        #wpnt-audio-cutter .wpnt-stat-row:last-child { border-bottom: none; }\n        #wpnt-audio-cutter .wpnt-badge {\n            display: inline-flex; align-items: center; padding: 4px 8px; border-radius: 4px; font-weight: 600; font-size: 0.75rem;\n        }\n        #wpnt-audio-cutter .wpnt-badge.success { background: rgba(16, 185, 129, 0.15); color: var(--wpnt-success); }\n        #wpnt-audio-cutter .custom-audio-player { width: 100%; margin-bottom: 16px; border-radius: var(--wpnt-radius-sm); outline: none; }\n\n        \/* Audio Cutter Specifics (Waveform & Layout) *\/\n        #wpnt-audio-cutter .wpnt-editor-container { display: none; margin-top: 24px; }\n        #wpnt-audio-cutter .wpnt-waveform-wrapper {\n            background: #0f172a; border-radius: var(--wpnt-radius-md); height: 150px;\n            position: relative; margin-bottom: 24px; overflow: hidden; box-shadow: var(--card-shadow);\n            cursor: crosshair; \/* Ditambahkan agar kursor berubah saat di-hover\/di-drag *\/\n            touch-action: none; \/* Mencegah layar HP terscroll saat menggeser kotak pemotong audio *\/\n        }\n        #waveform-canvas { width: 100%; height: 100%; display: block; }\n        #wpnt-audio-cutter .wpnt-selection-overlay {\n            position: absolute; top: 0; height: 100%; background: rgba(67, 97, 238, 0.3);\n            border-left: 2px solid var(--accent-color); border-right: 2px solid var(--accent-color); pointer-events: none;\n        }\n        #wpnt-audio-cutter .action-grid {\n            display: grid; grid-template-columns: 1fr 1fr; gap: 16px;\n        }\n\n        \/* Responsiveness *\/\n        @media (max-width: 600px) {\n            #wpnt-audio-cutter { padding: 20px 16px; }\n            #wpnt-audio-cutter h3 { font-size: 1.4rem; }\n            #wpnt-audio-cutter .wpnt-settings, #wpnt-audio-cutter .action-grid { grid-template-columns: 1fr; }\n        }\n    <\/style>\n<\/head>\n<body>\n\n<div id=\"wpnt-audio-cutter\">\n    <div class=\"wpnt-header\">\n        <h3>Fast Audio Cutter<\/h3>\n        <p>Trim your audio files instantly in your browser<\/p>\n        <div class=\"wpnt-privacy-badge\">\ud83d\udd12 Private: No files sent to server<\/div>\n    <\/div>\n\n    <!-- Area Upload -->\n    <div id=\"dropzone\" class=\"wpnt-dropzone\">\n        <span class=\"wpnt-icon-upload\">\ud83c\udfb5<\/span>\n        <p><strong>Click to upload<\/strong> or drag and drop<\/p>\n        <p style=\"font-size: 0.8rem; margin-top: 8px;\">Supports MP3, WAV, M4A, AAC<\/p>\n        <input type=\"file\" id=\"audio-input\" accept=\"audio\/*\">\n        <div class=\"file-info\" id=\"file-name-display\"><\/div>\n    <\/div>\n\n    <!-- Progress Bar (Loading \/ Processing) -->\n    <div class=\"progress-container\" id=\"progress-area\">\n        <div class=\"progress-label\" id=\"progress-text\">Loading audio&#8230;<\/div>\n        <div class=\"progress-bar-bg\"><div class=\"progress-bar-fill\" id=\"progress-bar\"><\/div><\/div>\n    <\/div>\n\n    <!-- Area Editor -->\n    <div id=\"editor\" class=\"wpnt-editor-container\">\n        <!-- Waveform Visualizer (Sekarang bisa di-drag!) -->\n        <div class=\"wpnt-waveform-wrapper\">\n            <canvas id=\"waveform-canvas\"><\/canvas>\n            <div id=\"selection-overlay\" class=\"wpnt-selection-overlay\"><\/div>\n        <\/div>\n\n        <!-- Input Waktu -->\n        <div class=\"wpnt-settings\">\n            <div class=\"wpnt-setting-group\">\n                <label class=\"wpnt-label\" for=\"start-time\">Start Time (s)<\/label>\n                <input type=\"number\" id=\"start-time\" class=\"wpnt-val\" value=\"0\" step=\"0.1\" min=\"0\">\n            <\/div>\n            <div class=\"wpnt-setting-group\">\n                <label class=\"wpnt-label\" for=\"end-time\">End Time (s)<\/label>\n                <input type=\"number\" id=\"end-time\" class=\"wpnt-val\" value=\"0\" step=\"0.1\" min=\"0\">\n            <\/div>\n            <div class=\"wpnt-setting-group\">\n                <label class=\"wpnt-label\" for=\"total-duration\">Duration (s)<\/label>\n                <input type=\"text\" id=\"total-duration\" class=\"wpnt-val\" readonly value=\"0.00\">\n            <\/div>\n        <\/div>\n\n        <!-- Tombol Aksi -->\n        <div class=\"action-grid\">\n            <button id=\"btn-play\" class=\"wpnt-btn wpnt-btn-secondary\">\u25b6 Play Selected<\/button>\n            <button id=\"btn-cut\" class=\"wpnt-btn\">\u2702 Cut Audio<\/button>\n        <\/div>\n    <\/div>\n\n    <!-- Area Hasil (Results) -->\n    <div class=\"wpnt-results\" id=\"results-area\">\n        <div class=\"wpnt-card\">\n            <audio controls class=\"custom-audio-player\" id=\"result-audio\"><\/audio>\n            \n            <div class=\"wpnt-stats\">\n                <div class=\"wpnt-stat-row\">\n                    <span>Original Duration<\/span>\n                    <span id=\"stat-original\">0.00s<\/span>\n                <\/div>\n                <div class=\"wpnt-stat-row\">\n                    <span>New Duration<\/span>\n                    <span id=\"stat-new\" style=\"font-weight:700; color:var(--accent-color);\">0.00s<\/span>\n                <\/div>\n                <div class=\"wpnt-stat-row\">\n                    <span>Status<\/span>\n                    <span class=\"wpnt-badge success\">\u2714 Success<\/span>\n                <\/div>\n            <\/div>\n\n            <a id=\"btn-download\" class=\"wpnt-btn\" download=\"cut_audio.wav\" style=\"margin-bottom: 12px;\">\ud83d\udcbe Download File (.wav)<\/a>\n            <button id=\"btn-reset\" class=\"wpnt-btn\" style=\"background:transparent; color:var(--wpnt-danger) !important; border:1px solid var(--wpnt-danger);\">Start Over<\/button>\n        <\/div>\n    <\/div>\n<\/div>\n\n<script>\n(function() {\n    \/\/ Variabel Global\n    let audioContext = null;\n    let audioBuffer = null;\n    let sourceNode = null;\n    let isPlaying = false;\n    let startTime = 0;\n    let endTime = 0;\n    let originalDuration = 0;\n    \n    \/\/ Variabel untuk fitur Drag (Geser)\n    let isDragging = false;\n    let dragStartMarker = 0;\n\n    \/\/ Elemen DOM\n    const dropzone = document.getElementById('dropzone');\n    const audioInput = document.getElementById('audio-input');\n    const fileNameDisplay = document.getElementById('file-name-display');\n    const progressArea = document.getElementById('progress-area');\n    const progressBar = document.getElementById('progress-bar');\n    const progressText = document.getElementById('progress-text');\n    const editor = document.getElementById('editor');\n    const resultsArea = document.getElementById('results-area');\n    \n    const waveformWrapper = document.querySelector('.wpnt-waveform-wrapper');\n    const canvas = document.getElementById('waveform-canvas');\n    const ctx = canvas.getContext('2d');\n    const overlay = document.getElementById('selection-overlay');\n    \n    const startInput = document.getElementById('start-time');\n    const endInput = document.getElementById('end-time');\n    const durationText = document.getElementById('total-duration');\n    const btnPlay = document.getElementById('btn-play');\n    const btnCut = document.getElementById('btn-cut');\n    \n    const statOriginal = document.getElementById('stat-original');\n    const statNew = document.getElementById('stat-new');\n    const resultAudio = document.getElementById('result-audio');\n    const btnDownload = document.getElementById('btn-download');\n\n    \/\/ Event Klik Dropzone\n    dropzone.onclick = () => audioInput.click();\n\n    \/\/ Event File Dipilih\n    audioInput.onchange = function(e) {\n        const file = e.target.files[0];\n        if (!file) return;\n        fileNameDisplay.innerText = file.name;\n        loadAudio(file);\n    };\n\n    \/\/ Proses Muat Audio\n    async function loadAudio(file) {\n        dropzone.style.display = 'none';\n        progressArea.style.display = 'block';\n        progressText.innerText = 'Decoding audio...';\n        progressBar.style.width = '40%';\n        \n        const reader = new FileReader();\n        reader.onload = async function(e) {\n            progressBar.style.width = '70%';\n            try {\n                if (!audioContext) audioContext = new (window.AudioContext || window.webkitAudioContext)();\n                audioBuffer = await audioContext.decodeAudioData(e.target.result);\n                setupEditor();\n            } catch (err) {\n                alert(\"Error decoding audio file. Please try another format.\");\n                location.reload();\n            }\n        };\n        reader.readAsArrayBuffer(file);\n    }\n\n    \/\/ Siapkan UI Editor\n    function setupEditor() {\n        progressBar.style.width = '100%';\n        setTimeout(() => {\n            progressArea.style.display = 'none';\n            editor.style.display = 'block';\n            \n            originalDuration = audioBuffer.duration;\n            startTime = 0;\n            endTime = originalDuration;\n            \n            syncUI(); \/\/ Update UI\n            drawWaveform();\n        }, 400);\n    }\n\n    \/\/ Menggambar Grafik Gelombang (Waveform)\n    function drawWaveform() {\n        const dpr = window.devicePixelRatio || 1;\n        canvas.width = canvas.offsetWidth * dpr;\n        canvas.height = canvas.offsetHeight * dpr;\n        ctx.scale(dpr, dpr);\n\n        const width = canvas.offsetWidth;\n        const height = canvas.offsetHeight;\n        const data = audioBuffer.getChannelData(0);\n        const step = Math.ceil(data.length \/ width);\n        const amp = height \/ 2;\n\n        ctx.fillStyle = '#4361ee'; \/\/ accent-color\n        ctx.clearRect(0, 0, width, height);\n\n        for (let i = 0; i < width; i++) {\n            let min = 1.0;\n            let max = -1.0;\n            for (let j = 0; j < step; j++) {\n                const datum = data[(i * step) + j];\n                if (datum < min) min = datum;\n                if (datum > max) max = datum;\n            }\n            ctx.fillRect(i, (1 + min) * amp, 1, Math.max(1, (max - min) * amp));\n        }\n    }\n\n    \/\/ --- FUNGSI SINKRONISASI UI (DISEDERHANAKAN) ---\n    function syncUI() {\n        startInput.value = startTime.toFixed(2);\n        endInput.value = endTime.toFixed(2);\n        durationText.value = (endTime - startTime).toFixed(2);\n        \n        const total = originalDuration || audioBuffer.duration;\n        const left = (startTime \/ total) * 100;\n        const right = 100 - (endTime \/ total) * 100;\n        overlay.style.left = left + '%';\n        overlay.style.right = right + '%';\n    }\n\n    \/\/ --- LOGIKA INTERAKSI GESER\/DRAG WAVEFORM (WP-SAFE) ---\n    function getTimeFromEvent(e) {\n        const rect = waveformWrapper.getBoundingClientRect();\n        \n        \/\/ WP-Safe touch detection (menghindari penggunaan simbol AND)\n        let clientX = e.clientX;\n        if (e.touches) {\n            if (e.touches.length > 0) {\n                clientX = e.touches[0].clientX;\n            }\n        }\n        \n        let x = clientX - rect.left;\n        x = Math.max(0, Math.min(x, rect.width)); \/\/ Batasi di dalam canvas\n        return (x \/ rect.width) * originalDuration;\n    }\n\n    function handleDragStart(e) {\n        if (!audioBuffer) return;\n        \/\/ Hanya prevent default untuk mouse agar tidak memilih teks, tapi izinkan touch untuk klik tombol\n        if (e.type === 'mousedown') e.preventDefault(); \n        \n        isDragging = true;\n        const time = getTimeFromEvent(e);\n        dragStartMarker = time;\n        startTime = time;\n        endTime = time;\n        syncUI();\n    }\n\n    function handleDragMove(e) {\n        if (!isDragging) return;\n        if (!audioBuffer) return;\n        \n        e.preventDefault(); \/\/ Mencegah scrolling halaman saat menggeser\n        \n        const time = getTimeFromEvent(e);\n        if (time < dragStartMarker) {\n            startTime = time;\n            endTime = dragStartMarker;\n        } else {\n            startTime = dragStartMarker;\n            endTime = time;\n        }\n        syncUI();\n    }\n\n    function handleDragEnd() {\n        if (!isDragging) return;\n        isDragging = false;\n        \n        \/\/ Pastikan ada durasi minimal (0.1 detik) jika pengguna cuma klik tanpa geser\n        if (endTime - startTime < 0.1) {\n            endTime = Math.min(startTime + 0.1, originalDuration);\n            \n            \/\/ WP-Safe logic (menghindari penggunaan simbol AND)\n            if (endTime === originalDuration) {\n                if (endTime - startTime < 0.1) {\n                    startTime = Math.max(0, endTime - 0.1);\n                }\n            }\n        }\n        syncUI();\n    }\n\n    \/\/ Daftarkan Event Listener untuk Mouse (PC)\n    waveformWrapper.addEventListener('mousedown', handleDragStart);\n    document.addEventListener('mousemove', handleDragMove, { passive: false });\n    document.addEventListener('mouseup', handleDragEnd);\n\n    \/\/ Daftarkan Event Listener untuk Sentuhan (Mobile)\n    waveformWrapper.addEventListener('touchstart', handleDragStart, { passive: false });\n    document.addEventListener('touchmove', handleDragMove, { passive: false });\n    document.addEventListener('touchend', handleDragEnd);\n\n\n    \/\/ Event Perubahan Angka Waktu Manual di Input Text\n    [startInput, endInput].forEach(el => {\n        el.onchange = () => {\n            startTime = parseFloat(startInput.value) || 0;\n            endTime = parseFloat(endInput.value) || originalDuration;\n            \n            if (startTime < 0) startTime = 0;\n            if (endTime > originalDuration) endTime = originalDuration;\n            if (startTime >= endTime) startTime = endTime - 0.1;\n            \n            syncUI();\n        };\n    });\n\n    \/\/ Fitur Pratinjau Suara (Play\/Stop)\n    btnPlay.onclick = function() {\n        if (isPlaying) {\n            stopAudio();\n            return;\n        }\n        const duration = endTime - startTime;\n        sourceNode = audioContext.createBufferSource();\n        sourceNode.buffer = audioBuffer;\n        sourceNode.connect(audioContext.destination);\n        sourceNode.start(0, startTime, duration);\n        \n        isPlaying = true;\n        btnPlay.innerText = \"\u23f9 Stop Preview\";\n        \n        sourceNode.onended = () => {\n            isPlaying = false;\n            btnPlay.innerText = \"\u25b6 Play Selected\";\n        };\n    };\n\n    function stopAudio() {\n        if (sourceNode) {\n            sourceNode.onended = null; \/\/ Mencegah trigger ganda\n            try { sourceNode.stop(); } catch(e) {}\n        }\n        isPlaying = false;\n        btnPlay.innerText = \"\u25b6 Play Selected\";\n    }\n\n    \/\/ Proses Pemotongan\n    btnCut.onclick = async function() {\n        stopAudio();\n        editor.style.display = 'none';\n        \n        \/\/ Tampilkan Loading Bar lagi\n        progressArea.style.display = 'block';\n        progressText.innerText = 'Cutting audio offline...';\n        progressBar.style.width = '50%';\n\n        setTimeout(async () => {\n            const duration = endTime - startTime;\n            const sampleRate = audioBuffer.sampleRate;\n            const offlineCtx = new OfflineAudioContext(\n                audioBuffer.numberOfChannels,\n                duration * sampleRate,\n                sampleRate\n            );\n\n            const source = offlineCtx.createBufferSource();\n            source.buffer = audioBuffer;\n            source.connect(offlineCtx.destination);\n            source.start(0, startTime, duration);\n\n            const renderedBuffer = await offlineCtx.startRendering();\n            const wavBlob = bufferToWav(renderedBuffer);\n            \n            progressBar.style.width = '100%';\n            \n            setTimeout(() => {\n                progressArea.style.display = 'none';\n                showResults(wavBlob, duration);\n            }, 500);\n            \n        }, 100);\n    };\n\n    \/\/ Tampilkan Kartu Hasil\n    function showResults(blob, newDuration) {\n        resultsArea.style.display = 'block';\n        \n        const url = URL.createObjectURL(blob);\n        resultAudio.src = url;\n        btnDownload.href = url;\n        \n        statOriginal.innerText = originalDuration.toFixed(2) + 's';\n        statNew.innerText = newDuration.toFixed(2) + 's';\n    }\n\n    \/\/ Fungsi Pengkodean WAV\n    function bufferToWav(abuffer) {\n        let numOfChan = abuffer.numberOfChannels,\n            length = abuffer.length * numOfChan * 2 + 44,\n            buffer = new ArrayBuffer(length),\n            view = new DataView(buffer),\n            channels = [], i, sample, offset = 0, pos = 0;\n\n        setUint32(0x46464952); setUint32(length - 8); setUint32(0x45564157);\n        setUint32(0x20746d66); setUint32(16); setUint16(1); setUint16(numOfChan);\n        setUint32(abuffer.sampleRate); setUint32(abuffer.sampleRate * 2 * numOfChan);\n        setUint16(numOfChan * 2); setUint16(16); setUint32(0x61746164); setUint32(length - pos - 4);\n\n        for(i = 0; i < abuffer.numberOfChannels; i++) channels.push(abuffer.getChannelData(i));\n\n        while(pos < length) {\n            for(i = 0; i < numOfChan; i++) {\n                sample = Math.max(-1, Math.min(1, channels[i][offset]));\n                sample = (sample < 0 ? sample * 0x8000 : sample * 0x7FFF);\n                view.setInt16(pos, sample, true); pos += 2;\n            }\n            offset++;\n        }\n\n        return new Blob([buffer], {type: \"audio\/wav\"});\n        function setUint16(data) { view.setUint16(pos, data, true); pos += 2; }\n        function setUint32(data) { view.setUint32(pos, data, true); pos += 4; }\n    }\n\n    \/\/ Tombol Reset \/ Mulai Ulang\n    document.getElementById('btn-reset').onclick = () => location.reload();\n\n})();\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Professional Audio Cutter Fast Audio Cutter Trim your audio files instantly in your browser \ud83d\udd12 Private: No files sent to server \ud83c\udfb5 Click to upload or drag and drop Supports MP3, WAV, M4A, AAC Loading audio&#8230; Start Time (s) End Time (s) Duration (s) \u25b6 Play Selected \u2702 Cut Audio Original Duration 0.00s New Duration [&hellip;]<\/p>\n","protected":false},"featured_media":0,"template":"","meta":[],"class_list":["post-68","tool","type-tool","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/tools.sanepo.com\/id\/wp-json\/wp\/v2\/tool\/68","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tools.sanepo.com\/id\/wp-json\/wp\/v2\/tool"}],"about":[{"href":"https:\/\/tools.sanepo.com\/id\/wp-json\/wp\/v2\/types\/tool"}],"wp:attachment":[{"href":"https:\/\/tools.sanepo.com\/id\/wp-json\/wp\/v2\/media?parent=68"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}