| 
							 | 
						<!DOCTYPE html> | 
					
					
						
						| 
							 | 
						<html lang="ja"> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						<head> | 
					
					
						
						| 
							 | 
						    <meta charset="UTF-8"> | 
					
					
						
						| 
							 | 
						    <script> | 
					
					
						
						| 
							 | 
						        function isAdmin() { | 
					
					
						
						| 
							 | 
						            return document.cookie.includes("admin_access=true"); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        if (!isAdmin()) { | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            (function(w, d, s, l, i) { | 
					
					
						
						| 
							 | 
						                w[l] = w[l] || []; | 
					
					
						
						| 
							 | 
						                w[l].push({ | 
					
					
						
						| 
							 | 
						                    'gtm.start': new Date().getTime(), | 
					
					
						
						| 
							 | 
						                    event: 'gtm.js' | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						                var f = d.getElementsByTagName(s)[0], | 
					
					
						
						| 
							 | 
						                    j = d.createElement(s), | 
					
					
						
						| 
							 | 
						                    dl = l != 'dataLayer' ? '&l=' + l : ''; | 
					
					
						
						| 
							 | 
						                j.async = true; | 
					
					
						
						| 
							 | 
						                j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; | 
					
					
						
						| 
							 | 
						                f.parentNode.insertBefore(j, f); | 
					
					
						
						| 
							 | 
						            })(window, document, 'script', 'dataLayer', 'GTM-NDL2LKLQ'); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            var gtagScript = document.createElement('script'); | 
					
					
						
						| 
							 | 
						            gtagScript.async = true; | 
					
					
						
						| 
							 | 
						            gtagScript.src = "https://www.googletagmanager.com/gtag/js?id=G-2M35HBEEVH"; | 
					
					
						
						| 
							 | 
						            document.head.appendChild(gtagScript); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            window.dataLayer = window.dataLayer || []; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            function gtag() { | 
					
					
						
						| 
							 | 
						                dataLayer.push(arguments); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						            gtag('js', new Date()); | 
					
					
						
						| 
							 | 
						            gtag('config', 'G-2M35HBEEVH'); | 
					
					
						
						| 
							 | 
						        } else { | 
					
					
						
						| 
							 | 
						            console.log("管理者のため、Googleタグはスキップされました。"); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    </script> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    <script src="https://unpkg.com/draggabilly@2/dist/draggabilly.pkgd.min.js"></script> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    <script> | 
					
					
						
						| 
							 | 
						        document.addEventListener('DOMContentLoaded', function() { | 
					
					
						
						| 
							 | 
						            (function() { | 
					
					
						
						| 
							 | 
						                const UPDATE_URL = '/update.txt'; | 
					
					
						
						| 
							 | 
						                const CHECK_INTERVAL_MS = 300000; | 
					
					
						
						| 
							 | 
						                let initialVersion = null; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const style = document.createElement('style'); | 
					
					
						
						| 
							 | 
						                style.textContent = ` | 
					
					
						
						| 
							 | 
						                    #update-popup { | 
					
					
						
						| 
							 | 
						                        position: fixed; | 
					
					
						
						| 
							 | 
						                        top: 20px; | 
					
					
						
						| 
							 | 
						                        right: 20px; | 
					
					
						
						| 
							 | 
						                        background: #fff3cd; | 
					
					
						
						| 
							 | 
						                        color: #856304; | 
					
					
						
						| 
							 | 
						                        border: 1px solid #ffeeba; | 
					
					
						
						| 
							 | 
						                        border-radius: 8px; | 
					
					
						
						| 
							 | 
						                        padding: 20px 16px 16px 16px; | 
					
					
						
						| 
							 | 
						                        box-shadow: 0 2px 10px rgba(0,0,0,0.1); | 
					
					
						
						| 
							 | 
						                        display: none; | 
					
					
						
						| 
							 | 
						                        z-index: 1000; | 
					
					
						
						| 
							 | 
						                        font-family: sans-serif; | 
					
					
						
						| 
							 | 
						                        width: 240px; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    #update-popup-close { | 
					
					
						
						| 
							 | 
						                        position: absolute; | 
					
					
						
						| 
							 | 
						                        top: 6px; | 
					
					
						
						| 
							 | 
						                        right: 8px; | 
					
					
						
						| 
							 | 
						                        background: none; | 
					
					
						
						| 
							 | 
						                        border: none; | 
					
					
						
						| 
							 | 
						                        font-size: 16px; | 
					
					
						
						| 
							 | 
						                        font-weight: bold; | 
					
					
						
						| 
							 | 
						                        color: #856404; | 
					
					
						
						| 
							 | 
						                        cursor: pointer; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    #update-popup-close:hover { | 
					
					
						
						| 
							 | 
						                        color: #000; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    #update-popup button.reload { | 
					
					
						
						| 
							 | 
						                        margin-top: 10px; | 
					
					
						
						| 
							 | 
						                        padding: 6px 12px; | 
					
					
						
						| 
							 | 
						                        border: none; | 
					
					
						
						| 
							 | 
						                        background-color: #ffc107; | 
					
					
						
						| 
							 | 
						                        color: #000; | 
					
					
						
						| 
							 | 
						                        cursor: pointer; | 
					
					
						
						| 
							 | 
						                        border-radius: 4px; | 
					
					
						
						| 
							 | 
						                        width: 100%; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    #update-popup button.reload:hover { | 
					
					
						
						| 
							 | 
						                        background-color: #e0a800; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                `; | 
					
					
						
						| 
							 | 
						                document.head.appendChild(style); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const popup = document.createElement('div'); | 
					
					
						
						| 
							 | 
						                popup.id = 'update-popup'; | 
					
					
						
						| 
							 | 
						                popup.innerHTML = ` | 
					
					
						
						| 
							 | 
						                    <button id="update-popup-close" aria-label="閉じる">×</button> | 
					
					
						
						| 
							 | 
						                    <p>ページが新しくなりました。<br>タブの再読み込みが必要です。</p> | 
					
					
						
						| 
							 | 
						                    <button class="reload">再読み込み</button> | 
					
					
						
						| 
							 | 
						                `; | 
					
					
						
						| 
							 | 
						                document.body.appendChild(popup); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                document.getElementById('update-popup-close').addEventListener('click', () => { | 
					
					
						
						| 
							 | 
						                    popup.style.display = 'none'; | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                popup.querySelector('.reload').addEventListener('click', () => { | 
					
					
						
						| 
							 | 
						                    location.reload(); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                async function fetchVersion() { | 
					
					
						
						| 
							 | 
						                    try { | 
					
					
						
						| 
							 | 
						                        const res = await fetch(UPDATE_URL + '?t=' + Date.now()); | 
					
					
						
						| 
							 | 
						                        if (res.ok) return await res.text(); | 
					
					
						
						| 
							 | 
						                    } catch (err) { | 
					
					
						
						| 
							 | 
						                        console.warn('update.txt の取得に失敗:', err); | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                    return null; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                async function checkForUpdate() { | 
					
					
						
						| 
							 | 
						                    const latest = await fetchVersion(); | 
					
					
						
						| 
							 | 
						                    if (initialVersion && latest && latest !== initialVersion) { | 
					
					
						
						| 
							 | 
						                        popup.style.display = 'block'; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                (async () => { | 
					
					
						
						| 
							 | 
						                    initialVersion = await fetchVersion(); | 
					
					
						
						| 
							 | 
						                    if (initialVersion) { | 
					
					
						
						| 
							 | 
						                        setInterval(checkForUpdate, CHECK_INTERVAL_MS); | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                })(); | 
					
					
						
						| 
							 | 
						            })(); | 
					
					
						
						| 
							 | 
						        }) | 
					
					
						
						| 
							 | 
						    </script> | 
					
					
						
						| 
							 | 
						    <link rel="icon" type="image/svg+xml" href='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIwMCAyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgbGFuZz0iamEiPjxkZWZzPiA8bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQxIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjAlIiB5Mj0iMTAwJSI+IDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzNDc0ZWIiIC8+IDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzM0YThlYiIgLz4gPC9saW5lYXJHcmFkaWVudD4gPC9kZWZzPiA8IS0tIOWQhOS6uuWbvuWkjeS9nOWumuS7tuWkjeaWsOWIl+OBjOaWsOWIl+OBq+OBhOOBq+OBvuOBq+OCi+OCieWbvuWkjeOBjOOCjOOCieOCk+ODq+ODvOODrOODs+ODg+OCiwotLT4gPHBvbHlnb24gcG9pbnRzPSI1MCw1MCA1MCwxNTAgMTUwLDEwMCIgZmlsbD0idXJsKCNncmFkMSkiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iMiIgLz48L3N2Zz4=' /> | 
					
					
						
						| 
							 | 
						    <meta charset="UTF-8"> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    <script> | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        document.addEventListener("DOMContentLoaded", () => { | 
					
					
						
						| 
							 | 
						            document.querySelectorAll("input[type='range']").forEach((slider) => { | 
					
					
						
						| 
							 | 
						                const bubble = document.createElement("div"); | 
					
					
						
						| 
							 | 
						                bubble.style.position = "absolute"; | 
					
					
						
						| 
							 | 
						                bubble.style.background = "#333a"; | 
					
					
						
						| 
							 | 
						                bubble.style.color = "#fff"; | 
					
					
						
						| 
							 | 
						                bubble.style.padding = "4px 8px"; | 
					
					
						
						| 
							 | 
						                bubble.style.borderRadius = "4px"; | 
					
					
						
						| 
							 | 
						                bubble.style.fontSize = "12px"; | 
					
					
						
						| 
							 | 
						                bubble.style.pointerEvents = "none"; | 
					
					
						
						| 
							 | 
						                bubble.style.whiteSpace = "nowrap"; | 
					
					
						
						| 
							 | 
						                bubble.style.transform = "translate(-50%, -120%)"; | 
					
					
						
						| 
							 | 
						                bubble.style.transition = "opacity 0.1s"; | 
					
					
						
						| 
							 | 
						                bubble.style.opacity = "0"; | 
					
					
						
						| 
							 | 
						                bubble.style.zIndex = "1000"; | 
					
					
						
						| 
							 | 
						                document.body.appendChild(bubble); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                let rect = null; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                slider.addEventListener("mouseenter", () => { | 
					
					
						
						| 
							 | 
						                    rect = slider.getBoundingClientRect(); | 
					
					
						
						| 
							 | 
						                    bubble.style.opacity = "1"; | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                slider.addEventListener("mouseleave", () => { | 
					
					
						
						| 
							 | 
						                    bubble.style.opacity = "0"; | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                slider.addEventListener("mousemove", (e) => { | 
					
					
						
						| 
							 | 
						                    if (!rect) rect = slider.getBoundingClientRect(); | 
					
					
						
						| 
							 | 
						                    const min = parseFloat(slider.min || 0); | 
					
					
						
						| 
							 | 
						                    const max = parseFloat(slider.max || 100); | 
					
					
						
						| 
							 | 
						                    const step = parseFloat(slider.step || 1); | 
					
					
						
						| 
							 | 
						                    const relativeX = e.clientX - rect.left; | 
					
					
						
						| 
							 | 
						                    const percent = Math.min(Math.max(relativeX / rect.width, 0), 1); | 
					
					
						
						| 
							 | 
						                    let value = min + (max - min) * percent; | 
					
					
						
						| 
							 | 
						                    value = Math.round(value / step) * step; | 
					
					
						
						| 
							 | 
						                    value = Number(value.toFixed(4));  | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    bubble.textContent = value; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    const pageX = e.pageX; | 
					
					
						
						| 
							 | 
						                    const pageY = window.scrollY + rect.top; | 
					
					
						
						| 
							 | 
						                    bubble.style.left = `${pageX}px`; | 
					
					
						
						| 
							 | 
						                    bubble.style.top = `${pageY}px`; | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                window.addEventListener("scroll", () => { | 
					
					
						
						| 
							 | 
						                    rect = slider.getBoundingClientRect(); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                window.addEventListener("resize", () => { | 
					
					
						
						| 
							 | 
						                    rect = slider.getBoundingClientRect(); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						    </script> | 
					
					
						
						| 
							 | 
						    <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIwMCAyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgbGFuZz0iamEiPjxkZWZzPiA8bGluZWFyR3JhZGllbnQgaWQ9ImdyYWQxIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjAlIiB5Mj0iMTAwJSI+IDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzNDc0ZWIiIC8+IDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzM0YThlYiIgLz4gPC9saW5lYXJHcmFkaWVudD4gPC9kZWZzPiA8IS0tIOWQhOS6uuWbvuWkjeS9nOWumuS7tuWkjeaWsOWIl+OBjOaWsOWIl+OBq+OBhOOBq+OBvuOBq+OCi+OCieWbvuWkjeOBjOOCjOOCieOCk+ODq+ODvOODrOODs+ODg+OCiwotLT4gPHBvbHlnb24gcG9pbnRzPSI1MCw1MCA1MCwxNTAgMTUwLDEwMCIgZmlsbD0idXJsKCNncmFkMSkiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iMiIgLz48L3N2Zz4="> | 
					
					
						
						| 
							 | 
						    <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 
					
					
						
						| 
							 | 
						    <link rel="preconnect" href="https://fonts.googleapis.com"> | 
					
					
						
						| 
							 | 
						    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | 
					
					
						
						| 
							 | 
						    <link href="https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c&display=swap" rel="stylesheet"> | 
					
					
						
						| 
							 | 
						    <title>文化発表会音声プレイヤー</title> | 
					
					
						
						| 
							 | 
						    <style> | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        body { | 
					
					
						
						| 
							 | 
						            font-family: "M PLUS Rounded 1c", 'Arial', sans-serif; | 
					
					
						
						| 
							 | 
						            background-color: #0f0f33; | 
					
					
						
						| 
							 | 
						            color: #e6f1ff; | 
					
					
						
						| 
							 | 
						            margin: 0; | 
					
					
						
						| 
							 | 
						            padding: 20px; | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            flex-direction: column; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            min-height: 100vh; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        h1 { | 
					
					
						
						| 
							 | 
						            color: #a2c2e8; | 
					
					
						
						| 
							 | 
						            text-align: center; | 
					
					
						
						| 
							 | 
						            margin-bottom: 30px; | 
					
					
						
						| 
							 | 
						            border-bottom: 1px solid #64ffda; | 
					
					
						
						| 
							 | 
						            padding-bottom: 10px; | 
					
					
						
						| 
							 | 
						            width: 100%; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .container { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            flex-direction: column; | 
					
					
						
						| 
							 | 
						            width: 95%; | 
					
					
						
						| 
							 | 
						            max-width: 1000px; | 
					
					
						
						| 
							 | 
						            background-color: rgba(17, 34, 64, 0.01); | 
					
					
						
						| 
							 | 
						            border-radius: 10px; | 
					
					
						
						| 
							 | 
						            padding: 20px; | 
					
					
						
						| 
							 | 
						            box-shadow: 0 0 20px rgba(100, 255, 218, 0.2); | 
					
					
						
						| 
							 | 
						            backdrop-filter: blur(2px); | 
					
					
						
						| 
							 | 
						            border: 1px solid rgba(100, 255, 218, 0.1); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .audio-controls { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            flex-direction: column; | 
					
					
						
						| 
							 | 
						            gap: 10px; | 
					
					
						
						| 
							 | 
						            margin-bottom: 15px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .audio-item { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            gap: 10px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .audio-item label { | 
					
					
						
						| 
							 | 
						            min-width: 50px; | 
					
					
						
						| 
							 | 
						            color: #9ab3d9; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .audio-slider { | 
					
					
						
						| 
							 | 
						            flex-grow: 1; | 
					
					
						
						| 
							 | 
						            height: 8px; | 
					
					
						
						| 
							 | 
						            -webkit-appearance: none; | 
					
					
						
						| 
							 | 
						            background: #1e2a47; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            outline: none; | 
					
					
						
						| 
							 | 
						            background-image: linear-gradient(#64ffda, #64ffda); | 
					
					
						
						| 
							 | 
						            background-size: 100% 100%; | 
					
					
						
						| 
							 | 
						            background-repeat: no-repeat; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .audio-slider::-webkit-slider-thumb { | 
					
					
						
						| 
							 | 
						            -webkit-appearance: none; | 
					
					
						
						| 
							 | 
						            width: 18px; | 
					
					
						
						| 
							 | 
						            height: 18px; | 
					
					
						
						| 
							 | 
						            background: #64ffda; | 
					
					
						
						| 
							 | 
						            border-radius: 10%; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .settings { | 
					
					
						
						| 
							 | 
						            background-color: rbga(30, 42, 71, 0.3); | 
					
					
						
						| 
							 | 
						            padding: 15px; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            margin-bottom: 20px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .setting-item { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            justify-content: space-between; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            margin-bottom: 10px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .setting-item:last-child { | 
					
					
						
						| 
							 | 
						            margin-bottom: 0; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .setting-item label { | 
					
					
						
						| 
							 | 
						            color: #ccd6f6; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .global-volume-container, | 
					
					
						
						| 
							 | 
						        .playback-speed-container { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            gap: 10px; | 
					
					
						
						| 
							 | 
						            width: 100%; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .global-volume-slider, | 
					
					
						
						| 
							 | 
						        .playback-speed-slider { | 
					
					
						
						| 
							 | 
						            flex-grow: 1; | 
					
					
						
						| 
							 | 
						            height: 8px; | 
					
					
						
						| 
							 | 
						            -webkit-appearance: none; | 
					
					
						
						| 
							 | 
						            background: #1e2a47; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            outline: none; | 
					
					
						
						| 
							 | 
						            background-image: linear-gradient(#64ffda, #64ffda); | 
					
					
						
						| 
							 | 
						            background-size: 100% 100%; | 
					
					
						
						| 
							 | 
						            background-repeat: no-repeat; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .global-volume-slider::-webkit-slider-thumb, | 
					
					
						
						| 
							 | 
						        .playback-speed-slider::-webkit-slider-thumb { | 
					
					
						
						| 
							 | 
						            -webkit-appearance: none; | 
					
					
						
						| 
							 | 
						            width: 16px; | 
					
					
						
						| 
							 | 
						            height: 16px; | 
					
					
						
						| 
							 | 
						            background: #64ffda; | 
					
					
						
						| 
							 | 
						            border-radius: 20%; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .slider-value { | 
					
					
						
						| 
							 | 
						            min-width: 40px; | 
					
					
						
						| 
							 | 
						            text-align: right; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        input[type="number"], | 
					
					
						
						| 
							 | 
						        input[type="checkbox"], | 
					
					
						
						| 
							 | 
						        select { | 
					
					
						
						| 
							 | 
						            background-color: #112240; | 
					
					
						
						| 
							 | 
						            border: 1px solid #64ffda; | 
					
					
						
						| 
							 | 
						            color: #e6f1ff; | 
					
					
						
						| 
							 | 
						            padding: 5px; | 
					
					
						
						| 
							 | 
						            border-radius: 3px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .tech-decoration { | 
					
					
						
						| 
							 | 
						            width: 100%; | 
					
					
						
						| 
							 | 
						            height: 2px; | 
					
					
						
						| 
							 | 
						            background: linear-gradient(90deg, transparent, #64ffda, transparent); | 
					
					
						
						| 
							 | 
						            margin: 20px 0; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        .loading-overlay { | 
					
					
						
						| 
							 | 
						            position: fixed; | 
					
					
						
						| 
							 | 
						            top: 0; | 
					
					
						
						| 
							 | 
						            left: 0; | 
					
					
						
						| 
							 | 
						            width: 100%; | 
					
					
						
						| 
							 | 
						            height: 100%; | 
					
					
						
						| 
							 | 
						            background-color: rgba(0, 0, 0, 0.8); | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            justify-content: center; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            z-index: 9998; | 
					
					
						
						| 
							 | 
						            transition: opacity 1s ease-out; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .spinner-box { | 
					
					
						
						| 
							 | 
						            width: 300px; | 
					
					
						
						| 
							 | 
						            height: 300px; | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            justify-content: center; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            background-color: transparent; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        .leo { | 
					
					
						
						| 
							 | 
						            position: absolute; | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            justify-content: center; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            border-radius: 50%; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .blue-orbit { | 
					
					
						
						| 
							 | 
						            width: 165px; | 
					
					
						
						| 
							 | 
						            height: 165px; | 
					
					
						
						| 
							 | 
						            border: 1px solid #91daffa5; | 
					
					
						
						| 
							 | 
						            animation: spin3D 3s linear .2s infinite; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .green-orbit { | 
					
					
						
						| 
							 | 
						            width: 120px; | 
					
					
						
						| 
							 | 
						            height: 120px; | 
					
					
						
						| 
							 | 
						            border: 1px solid #91ffbfa5; | 
					
					
						
						| 
							 | 
						            animation: spin3D 2s linear 0s infinite; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .red-orbit { | 
					
					
						
						| 
							 | 
						            width: 90px; | 
					
					
						
						| 
							 | 
						            height: 90px; | 
					
					
						
						| 
							 | 
						            border: 1px solid #ffca91a5; | 
					
					
						
						| 
							 | 
						            animation: spin3D 1s linear 0s infinite; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .white-orbit { | 
					
					
						
						| 
							 | 
						            width: 60px; | 
					
					
						
						| 
							 | 
						            height: 60px; | 
					
					
						
						| 
							 | 
						            border: 2px solid #ffffff; | 
					
					
						
						| 
							 | 
						            animation: spin3D 10s linear 0s infinite; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .w1 { | 
					
					
						
						| 
							 | 
						            transform: rotate3D(1, 1, 1, 90deg); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .w2 { | 
					
					
						
						| 
							 | 
						            transform: rotate3D(1, 2, .5, 90deg); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .w3 { | 
					
					
						
						| 
							 | 
						            transform: rotate3D(.5, 1, 2, 90deg); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        @keyframes spin3D { | 
					
					
						
						| 
							 | 
						            from { | 
					
					
						
						| 
							 | 
						                transform: rotate3d(.5, .5, .5, 360deg); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            to { | 
					
					
						
						| 
							 | 
						                transform: rotate3d(0, 0, 0, 0deg); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-set-button { | 
					
					
						
						| 
							 | 
						            background-color: #112240; | 
					
					
						
						| 
							 | 
						            border: 1px solid #64ffda; | 
					
					
						
						| 
							 | 
						            color: #e6f1ff; | 
					
					
						
						| 
							 | 
						            padding: 5px 10px; | 
					
					
						
						| 
							 | 
						            border-radius: 3px; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						            font-size: 12px; | 
					
					
						
						| 
							 | 
						            margin-left: 5px; | 
					
					
						
						| 
							 | 
						            transition: background-color 0.3s; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-set-button:hover { | 
					
					
						
						| 
							 | 
						            background-color: rgba(100, 255, 218, 0.2); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        .combine-button { | 
					
					
						
						| 
							 | 
						            background-color: #97c2f0; | 
					
					
						
						| 
							 | 
						            color: #0a192f; | 
					
					
						
						| 
							 | 
						            border: none; | 
					
					
						
						| 
							 | 
						            padding: 10px 20px; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            font-size: 16px; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						            margin-top: 20px; | 
					
					
						
						| 
							 | 
						            transition: all 0.3s; | 
					
					
						
						| 
							 | 
						            font-weight: bold; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .combine-button:hover { | 
					
					
						
						| 
							 | 
						            background-color: #52e0c4; | 
					
					
						
						| 
							 | 
						            transform: translateY(-2px); | 
					
					
						
						| 
							 | 
						            box-shadow: 0 5px 15px rgba(100, 255, 218, 0.4); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .combine-button:disabled { | 
					
					
						
						| 
							 | 
						            background-color: #3a5a78; | 
					
					
						
						| 
							 | 
						            cursor: not-allowed; | 
					
					
						
						| 
							 | 
						            transform: none; | 
					
					
						
						| 
							 | 
						            box-shadow: none; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        .combine-status { | 
					
					
						
						| 
							 | 
						            margin-top: 10px; | 
					
					
						
						| 
							 | 
						            color: #97c2f0; | 
					
					
						
						| 
							 | 
						            font-size: 14px; | 
					
					
						
						| 
							 | 
						            height: 20px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        .preview-section { | 
					
					
						
						| 
							 | 
						            margin-top: 20px; | 
					
					
						
						| 
							 | 
						            padding: 15px; | 
					
					
						
						| 
							 | 
						            background-color: rgba(17, 34, 64, 0.7); | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            display: none; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .preview-section h3 { | 
					
					
						
						| 
							 | 
						            margin-top: 0; | 
					
					
						
						| 
							 | 
						            color: #97c2f0; | 
					
					
						
						| 
							 | 
						            border-bottom: 1px solid #64ffda; | 
					
					
						
						| 
							 | 
						            padding-bottom: 5px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        .disabled-overlay { | 
					
					
						
						| 
							 | 
						            position: absolute; | 
					
					
						
						| 
							 | 
						            top: 0; | 
					
					
						
						| 
							 | 
						            left: 0; | 
					
					
						
						| 
							 | 
						            width: 100%; | 
					
					
						
						| 
							 | 
						            height: 100%; | 
					
					
						
						| 
							 | 
						            background-color: rgba(10, 25, 47, 0.7); | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            justify-content: center; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            z-index: 10; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .disabled-message { | 
					
					
						
						| 
							 | 
						            background-color: rgba(30, 42, 71, 0.9); | 
					
					
						
						| 
							 | 
						            padding: 20px; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            text-align: center; | 
					
					
						
						| 
							 | 
						            max-width: 80%; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .disabled-message p { | 
					
					
						
						| 
							 | 
						            margin-bottom: 15px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .loader { | 
					
					
						
						| 
							 | 
						            width: 80px; | 
					
					
						
						| 
							 | 
						            aspect-ratio: 1; | 
					
					
						
						| 
							 | 
						            border: 10px solid #000; | 
					
					
						
						| 
							 | 
						            box-sizing: border-box; | 
					
					
						
						| 
							 | 
						            background: | 
					
					
						
						| 
							 | 
						                radial-gradient(farthest-side, #fff 98%, #0000) 50%/20px 20px, | 
					
					
						
						| 
							 | 
						                radial-gradient(farthest-side, #fff 98%, #0000) 50%/20px 20px, | 
					
					
						
						| 
							 | 
						                radial-gradient(farthest-side, #fff 98%, #0000) 50%/20px 20px, | 
					
					
						
						| 
							 | 
						                radial-gradient(farthest-side, #fff 98%, #0000) 50%/20px 20px, | 
					
					
						
						| 
							 | 
						                radial-gradient(farthest-side, #fff 98%, #0000) 50%/80% 80%, | 
					
					
						
						| 
							 | 
						                #000; | 
					
					
						
						| 
							 | 
						            background-repeat: no-repeat; | 
					
					
						
						| 
							 | 
						            filter: blur(4px) contrast(10); | 
					
					
						
						| 
							 | 
						            animation: squarePulse 1s infinite alternate; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        @keyframes squarePulse { | 
					
					
						
						| 
							 | 
						            0% { | 
					
					
						
						| 
							 | 
						                background-position: | 
					
					
						
						| 
							 | 
						                    50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            25% { | 
					
					
						
						| 
							 | 
						                background-position: | 
					
					
						
						| 
							 | 
						                    50% 0, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            50% { | 
					
					
						
						| 
							 | 
						                background-position: | 
					
					
						
						| 
							 | 
						                    50% 0, 50% 100%, 50% 50%, 50% 50%, 50% 50%, 50% 50%; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            75% { | 
					
					
						
						| 
							 | 
						                background-position: | 
					
					
						
						| 
							 | 
						                    50% 0, 50% 100%, 0 50%, 50% 50%, 50% 50%, 50% 50%; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            100% { | 
					
					
						
						| 
							 | 
						                background-position: | 
					
					
						
						| 
							 | 
						                    50% 0, 50% 100%, 0 50%, 100% 50%, 50% 50%, 50% 50%; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .sync-status { | 
					
					
						
						| 
							 | 
						            position: absolute; | 
					
					
						
						| 
							 | 
						            bottom: 100px; | 
					
					
						
						| 
							 | 
						            left: 10px; | 
					
					
						
						| 
							 | 
						            width: 150px; | 
					
					
						
						| 
							 | 
						            height: 30px; | 
					
					
						
						| 
							 | 
						            background-color: rgba(0, 0, 0, 0.7); | 
					
					
						
						| 
							 | 
						            color: #97c2f0; | 
					
					
						
						| 
							 | 
						            padding: 5px 10px; | 
					
					
						
						| 
							 | 
						            border-radius: 3px; | 
					
					
						
						| 
							 | 
						            font-size: 12px; | 
					
					
						
						| 
							 | 
						            z-index: 5; | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            gap: 5px; | 
					
					
						
						| 
							 | 
						            white-space: nowrap; | 
					
					
						
						| 
							 | 
						            overflow: hidden; | 
					
					
						
						| 
							 | 
						            text-overflow: ellipsis; | 
					
					
						
						| 
							 | 
						            user-select: none; | 
					
					
						
						| 
							 | 
						            contain: strict; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .sync-status button { | 
					
					
						
						| 
							 | 
						            background: none; | 
					
					
						
						| 
							 | 
						            border: none; | 
					
					
						
						| 
							 | 
						            color: #fff; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						            font-size: 12px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-markers-container { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            flex-wrap: wrap; | 
					
					
						
						| 
							 | 
						            gap: 10px; | 
					
					
						
						| 
							 | 
						            margin: 15px 0; | 
					
					
						
						| 
							 | 
						            padding: 15px; | 
					
					
						
						| 
							 | 
						            background-color: rgba(17, 34, 64, 0.8); | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-marker { | 
					
					
						
						| 
							 | 
						            font-size: 80%; | 
					
					
						
						| 
							 | 
						            padding: 6px 8px; | 
					
					
						
						| 
							 | 
						            background-color: rgba(100, 255, 218, 0.05); | 
					
					
						
						| 
							 | 
						            border: 1px solid #64ffda; | 
					
					
						
						| 
							 | 
						            border-radius: 4px; | 
					
					
						
						| 
							 | 
						            cursor: grab; | 
					
					
						
						| 
							 | 
						            user-select: none; | 
					
					
						
						| 
							 | 
						            transition: background-color 0.3s; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-marker:hover { | 
					
					
						
						| 
							 | 
						            background-color: rgba(100, 255, 218, 0.1); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-marker.dragging { | 
					
					
						
						| 
							 | 
						            opacity: 0.7; | 
					
					
						
						| 
							 | 
						            cursor: grabbing; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-input-container { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            gap: 5px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-input-container input { | 
					
					
						
						| 
							 | 
						            width: 80px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        .player-controls { | 
					
					
						
						| 
							 | 
						            background-color: rgba(17, 34, 64, 0.2); | 
					
					
						
						| 
							 | 
						            padding: 10px; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            flex-direction: column; | 
					
					
						
						| 
							 | 
						            gap: 10px; | 
					
					
						
						| 
							 | 
						            transition: opacity 0.3s; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .progress-container { | 
					
					
						
						| 
							 | 
						            width: 100%; | 
					
					
						
						| 
							 | 
						            height: 10px; | 
					
					
						
						| 
							 | 
						            background-color: #1e2a47; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						            position: relative; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .progress-bar { | 
					
					
						
						| 
							 | 
						            height: 100%; | 
					
					
						
						| 
							 | 
						            background-color: #64ffda; | 
					
					
						
						| 
							 | 
						            border-radius: 5px; | 
					
					
						
						| 
							 | 
						            width: 0%; | 
					
					
						
						| 
							 | 
						            position: relative; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .progress-time { | 
					
					
						
						| 
							 | 
						            position: absolute; | 
					
					
						
						| 
							 | 
						            top: -25px; | 
					
					
						
						| 
							 | 
						            transform: translateX(-50%); | 
					
					
						
						| 
							 | 
						            background-color: rgba(30, 42, 71, 0.9); | 
					
					
						
						| 
							 | 
						            padding: 3px 6px; | 
					
					
						
						| 
							 | 
						            border-radius: 3px; | 
					
					
						
						| 
							 | 
						            font-size: 12px; | 
					
					
						
						| 
							 | 
						            display: none; | 
					
					
						
						| 
							 | 
						            white-space: nowrap; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .progress-marker { | 
					
					
						
						| 
							 | 
						            position: absolute; | 
					
					
						
						| 
							 | 
						            bottom: 0px; | 
					
					
						
						| 
							 | 
						            width: 0; | 
					
					
						
						| 
							 | 
						            height: 0; | 
					
					
						
						| 
							 | 
						            border-left: 5px solid transparent; | 
					
					
						
						| 
							 | 
						            border-right: 5px solid transparent; | 
					
					
						
						| 
							 | 
						            border-top: 10px solid #ff5555; | 
					
					
						
						| 
							 | 
						            transform: translateX(-50%); | 
					
					
						
						| 
							 | 
						            z-index: 2; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .main-controls { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            gap: 15px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .control-button { | 
					
					
						
						| 
							 | 
						            background: none; | 
					
					
						
						| 
							 | 
						            border: none; | 
					
					
						
						| 
							 | 
						            color: #e6f1ff; | 
					
					
						
						| 
							 | 
						            font-size: 18px; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						            padding: 5px; | 
					
					
						
						| 
							 | 
						            border-radius: 30%; | 
					
					
						
						| 
							 | 
						            width: 36px; | 
					
					
						
						| 
							 | 
						            height: 36px; | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            justify-content: center; | 
					
					
						
						| 
							 | 
						            transition: background-color 0.3s; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .control-button:hover { | 
					
					
						
						| 
							 | 
						            background-color: rgba(100, 255, 218, 0.2); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .time-display { | 
					
					
						
						| 
							 | 
						            font-size: 14px; | 
					
					
						
						| 
							 | 
						            color: #ccd6f6; | 
					
					
						
						| 
							 | 
						            white-space: nowrap; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .volume-control { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            gap: 5px; | 
					
					
						
						| 
							 | 
						            margin-left: auto; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .volume-button { | 
					
					
						
						| 
							 | 
						            background: none; | 
					
					
						
						| 
							 | 
						            border: none; | 
					
					
						
						| 
							 | 
						            color: #e6f1ff; | 
					
					
						
						| 
							 | 
						            font-size: 18px; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						            padding: 5px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .volume-slider { | 
					
					
						
						| 
							 | 
						            width: 80px; | 
					
					
						
						| 
							 | 
						            height: 6px; | 
					
					
						
						| 
							 | 
						            -webkit-appearance: none; | 
					
					
						
						| 
							 | 
						            background: #1e2a47; | 
					
					
						
						| 
							 | 
						            border-radius: 3px; | 
					
					
						
						| 
							 | 
						            outline: none; | 
					
					
						
						| 
							 | 
						            opacity: 0; | 
					
					
						
						| 
							 | 
						            transition: opacity 0.3s, width 0.3s; | 
					
					
						
						| 
							 | 
						            background-image: linear-gradient(#6aebfc, #6aebfc); | 
					
					
						
						| 
							 | 
						            background-size: 100% 100%; | 
					
					
						
						| 
							 | 
						            background-repeat: no-repeat; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .volume-control:hover .volume-slider { | 
					
					
						
						| 
							 | 
						            opacity: 1; | 
					
					
						
						| 
							 | 
						            width: 100px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .volume-slider::-webkit-slider-thumb { | 
					
					
						
						| 
							 | 
						            -webkit-appearance: none; | 
					
					
						
						| 
							 | 
						            width: 12px; | 
					
					
						
						| 
							 | 
						            height: 12px; | 
					
					
						
						| 
							 | 
						            background: #6aebfc; | 
					
					
						
						| 
							 | 
						            border-radius: 30%; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .speed-control { | 
					
					
						
						| 
							 | 
						            display: flex; | 
					
					
						
						| 
							 | 
						            align-items: center; | 
					
					
						
						| 
							 | 
						            gap: 5px; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .speed-slider { | 
					
					
						
						| 
							 | 
						            width: 120px; | 
					
					
						
						| 
							 | 
						            height: 6px; | 
					
					
						
						| 
							 | 
						            -webkit-appearance: none; | 
					
					
						
						| 
							 | 
						            background: #1e2a47; | 
					
					
						
						| 
							 | 
						            border-radius: 3px; | 
					
					
						
						| 
							 | 
						            outline: none; | 
					
					
						
						| 
							 | 
						            background-image: linear-gradient(#64d1ff, #64d1ff); | 
					
					
						
						| 
							 | 
						            background-size: 100% 100%; | 
					
					
						
						| 
							 | 
						            background-repeat: no-repeat; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .speed-slider::-webkit-slider-thumb { | 
					
					
						
						| 
							 | 
						            -webkit-appearance: none; | 
					
					
						
						| 
							 | 
						            width: 12px; | 
					
					
						
						| 
							 | 
						            height: 12px; | 
					
					
						
						| 
							 | 
						            background: #5bb7de; | 
					
					
						
						| 
							 | 
						            border-radius: 20%; | 
					
					
						
						| 
							 | 
						            cursor: pointer; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        .speed-value { | 
					
					
						
						| 
							 | 
						            font-size: 14px; | 
					
					
						
						| 
							 | 
						            min-width: 30px; | 
					
					
						
						| 
							 | 
						            text-align: center; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    </style> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						</head> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						<body> | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-NDL2LKLQ" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    <div class="loading-overlay" id="loadingOverlay"> | 
					
					
						
						| 
							 | 
						        <div class="spinner-box"> | 
					
					
						
						| 
							 | 
						            <div class="blue-orbit leo"> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="green-orbit leo"> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="red-orbit leo"> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="white-orbit w1 leo"> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="white-orbit w2 leo"> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="white-orbit w3 leo"> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						        </div> | 
					
					
						
						| 
							 | 
						    </div> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    <h1 id="title-name">文化発表会音声プレイヤー</h1> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        <div class="details-container"> | 
					
					
						
						| 
							 | 
						        <details id="usageDetails"> | 
					
					
						
						| 
							 | 
						            <summary style="font-size: 20px">使い方</summary> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <h3>音声の合成</h3> | 
					
					
						
						| 
							 | 
						            <p><b>一番最初に「音声コントロール」で、スライダーを使用して各パートの音量を調整してください。</b>「合成音量係数」は全体の音量を上げます。<br> | 
					
					
						
						| 
							 | 
						                自分のパートの音量を1、それ以外のパートの音量を0.3くらいにすると他のパートのタイミングを確認しながら練習できます。<br> | 
					
					
						
						| 
							 | 
						                <b>調整が終わると「音声を合成」を押してください。</b></p> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <hr> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <h3>プレイヤーの使い方</h3> | 
					
					
						
						| 
							 | 
						            <p>「▶」や「⏸」ボタンで再生や一時停止ができます。「↺」で最初から再生できます。音量スライダーで音量を変更できます。スライダーで再生速度も変更できます。</p> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <hr> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <h3>高度な繰り返し機能</h3> | 
					
					
						
						| 
							 | 
						            <p>特定の秒数の間を再生したい場合は、再生を開始する秒数を「再生開始秒数」、再生を終了する秒数を「再生終了秒数」を設定してください。ドラッグアンドドロップで楽譜に載っている区間を設定できます。<br> | 
					
					
						
						| 
							 | 
						                ループがオンの場合は再生終了秒数になると再生開始秒数に戻ります。<br> | 
					
					
						
						| 
							 | 
						                設定ができたら「時間設定を適用」ボタンを押してください。</p> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <hr> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <h3>高度な設定</h3> | 
					
					
						
						| 
							 | 
						            <ul> | 
					
					
						
						| 
							 | 
						                <li>全体音量係数:全体の音量を上げたり下げたりできます。10が最大です。</li> | 
					
					
						
						| 
							 | 
						                <li>再生速度:再生速度を変更できます。</li> | 
					
					
						
						| 
							 | 
						                <li>テンポ:テンポを入力すると、そのテンポになるように再生速度を自動調整します。</li> | 
					
					
						
						| 
							 | 
						            </ul> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <hr> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <h3>オフライン設定</h3> | 
					
					
						
						| 
							 | 
						            <p>ページをオフラインで使用できるようにします。ただし、保存されるキャッシュが古い場合、更新プログラムがなかったため、正常に動作しない可能性があります。現在はこのバグは更新ポップアップを表示することで修正されました。</p> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						        </details> | 
					
					
						
						| 
							 | 
						    </div> | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    <script> | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        document.addEventListener("DOMContentLoaded", () => { | 
					
					
						
						| 
							 | 
						            const details = document.getElementById("usageDetails"); | 
					
					
						
						| 
							 | 
						            const openedBefore = localStorage.getItem("usageDetailsOpened"); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if(!openedBefore) { | 
					
					
						
						| 
							 | 
						                details.setAttribute("open", "true"); | 
					
					
						
						| 
							 | 
						                localStorage.setItem("usageDetailsOpened", "true"); | 
					
					
						
						| 
							 | 
						            } else { | 
					
					
						
						| 
							 | 
						                details.removeAttribute("open"); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						    </script> | 
					
					
						
						| 
							 | 
						        <div class="audio-controls"> | 
					
					
						
						| 
							 | 
						        <details> | 
					
					
						
						| 
							 | 
						            <summary style="font-size: 1.2em;">オフライン設定</summary> | 
					
					
						
						| 
							 | 
						            <div class="swsettings" id="swsettings"> | 
					
					
						
						| 
							 | 
						                <a>下のボタンを押すと、サービスワーカーでページをオフラインで使用できるようにします。<br> | 
					
					
						
						| 
							 | 
						                    ブラウザの仕様により、オフラインで開く際に<b>最初のみ二回再読み込みボタンを押さなければいけません。</b><br> | 
					
					
						
						| 
							 | 
						                    不安定な機能で、ページの更新などができなくなるバグが発生する可能性があります。</a> | 
					
					
						
						| 
							 | 
						                <br> | 
					
					
						
						| 
							 | 
						                <button class="combine-button" id="sw-register-btn" disabled>登録を開始</button> | 
					
					
						
						| 
							 | 
						                <div class="combine-status" id="sw-status"> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						        </details><br> | 
					
					
						
						| 
							 | 
						            <h2>音声コントロール</h2> | 
					
					
						
						| 
							 | 
						            <span>各パートの音の大きさを設定できます。自分のパートの音量を1、それ以外のパートの音量を0.3くらいにすると他のパートのタイミングを確認しながら練習できます。<br> | 
					
					
						
						| 
							 | 
						                ※「ピアノ」と「全体」は他のパートと音がずれるため、組み合わせて使用しないでください。</span> | 
					
					
						
						| 
							 | 
						            <div class="audio-item"> | 
					
					
						
						| 
							 | 
						                <label>ソプラノ</label> | 
					
					
						
						| 
							 | 
						                <input type="range" class="audio-slider" data-audio="s" min="0" max="1" step="0.01" value="1"> | 
					
					
						
						| 
							 | 
						                <span class="slider-value volume-value">1.00</span> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="audio-item"> | 
					
					
						
						| 
							 | 
						                <label>アルト </label> | 
					
					
						
						| 
							 | 
						                <input type="range" class="audio-slider" data-audio="a" min="0" max="1" step="0.01" value="1"> | 
					
					
						
						| 
							 | 
						                <span class="slider-value volume-value">1.00</span> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="audio-item"> | 
					
					
						
						| 
							 | 
						                <label>テノール</label> | 
					
					
						
						| 
							 | 
						                <input type="range" class="audio-slider" data-audio="t" min="0" max="1" step="0.01" value="1"> | 
					
					
						
						| 
							 | 
						                <span class="slider-value volume-value">1.00</span> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="audio-item"> | 
					
					
						
						| 
							 | 
						                <label>ピアノ </label> | 
					
					
						
						| 
							 | 
						                <input type="range" class="audio-slider" data-audio="p" min="0" max="1" step="0.01" value="1"> | 
					
					
						
						| 
							 | 
						                <span class="slider-value volume-value">1.00</span> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="audio-item"> | 
					
					
						
						| 
							 | 
						                <label>全体(非推奨)</label> | 
					
					
						
						| 
							 | 
						                <input type="range" class="audio-slider" data-audio="k" min="0" max="1" step="0.01" value="0"> | 
					
					
						
						| 
							 | 
						                <span class="slider-value volume-value">0.00</span> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <h3>FFmpeg合成用音量設定</h3> | 
					
					
						
						| 
							 | 
						            <span>FFmpegで音声を合成する際の音量設定です。通常は1.0で問題ありません。</span> | 
					
					
						
						| 
							 | 
						            <div class="audio-item"> | 
					
					
						
						| 
							 | 
						                <label>合成音量係数:</label> | 
					
					
						
						| 
							 | 
						                <input type="range" class="audio-slider" id="ffmpeg-volume" min="0" max="20" step="0.01" value="1"> | 
					
					
						
						| 
							 | 
						                <span class="slider-value" id="ffmpeg-volume-value">1.00</span> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            <button class="combine-button" id="combine-button">音声を合成</button> | 
					
					
						
						| 
							 | 
						            <div class="combine-status" id="combine-status"> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						        </div> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						  <div class="container"> | 
					
					
						
						| 
							 | 
						        <div class="tech-decoration"> | 
					
					
						
						| 
							 | 
						        </div> | 
					
					
						
						| 
							 | 
						<div class="sync-status" id="sync-status" style="display: none;"> | 
					
					
						
						| 
							 | 
						    <span id="sync-status-text">同期状態</span> | 
					
					
						
						| 
							 | 
						    <button id="sync-status-close" aria-label="閉じる">×</button> | 
					
					
						
						| 
							 | 
						</div> | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        <div class="preview-section" id="preview-section" hidden> | 
					
					
						
						| 
							 | 
						            <h3 hidden>プレビュー</h3> | 
					
					
						
						| 
							 | 
						            <p hidden>合成された音声をプレビューできます。再生ボタンをクリックして確認してください。</p> | 
					
					
						
						| 
							 | 
						            <button class="control-button" id="preview-button" hidden>▶</button> | 
					
					
						
						| 
							 | 
						            <span id="preview-time" hidden>00:00 / 00:00</span> | 
					
					
						
						| 
							 | 
						        </div> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        <div class="player-controls" id="player-controls"> | 
					
					
						
						| 
							 | 
						            <div class="disabled-overlay" id="disabledOverlay" style="display: block;"> | 
					
					
						
						| 
							 | 
						                <div class="disabled-message"> | 
					
					
						
						| 
							 | 
						                    <p>「合成」ボタンで音声を合成してください。</p> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="progress-container" id="progress-container"> | 
					
					
						
						| 
							 | 
						                <div class="progress-bar" id="progress-bar"> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						                <div class="progress-time" id="progress-time">00:00</div> | 
					
					
						
						| 
							 | 
						                <div class="progress-marker" id="start-marker" style="left: 0%; display: none;"> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						                <div class="progress-marker" id="end-marker" style="left: 100%; display: none;"> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="main-controls"> | 
					
					
						
						| 
							 | 
						                <button class="control-button" id="play-pause-btn" disabled>▶</button> | 
					
					
						
						| 
							 | 
						                <button class="control-button" id="reset-btn" disabled title="最初から再生">↺</button> | 
					
					
						
						| 
							 | 
						                <div class="time-display" id="time-display">00:00.00 / 00:00.00</div> | 
					
					
						
						| 
							 | 
						                <div class="volume-control"> | 
					
					
						
						| 
							 | 
						                    <button class="volume-button" id="volume-btn" disabled>🔊</button> | 
					
					
						
						| 
							 | 
						                    <input type="range" class="volume-slider" id="volume-slider" min="0" max="1" step="0.01" value="1" disabled> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						                <div class="speed-control"> | 
					
					
						
						| 
							 | 
						                    <span class="speed-value" id="speed-value">1.00x</span> | 
					
					
						
						| 
							 | 
						                    <input type="range" class="speed-slider" id="speed-slider" min="0.01" max="5" step="0.01" value="1" disabled> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						        </div> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        <div class="tech-decoration"> | 
					
					
						
						| 
							 | 
						        </div> | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        <div class="settings"> | 
					
					
						
						| 
							 | 
						            <div class="time-markers-container" id="time-markers-container"> | 
					
					
						
						| 
							 | 
						                <span>このボックスをドラッグして「再生開始秒数」や「再生終了秒数」の入力ボックスの上で離すと秒数を簡単に変更できます。各秒数は楽譜に合わせています。<br> | 
					
					
						
						| 
							 | 
						                    時の旅人はAからE、地球星歌は①から⑤で区切っていて、地球星歌はリピート記号があるので①と②は1番と2番があります。</span> | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="setting-item"> | 
					
					
						
						| 
							 | 
						                <label for="start-time">再生開始秒数:</label> | 
					
					
						
						| 
							 | 
						                <div class="time-input-container"> | 
					
					
						
						| 
							 | 
						                    <input type="number" id="start-time" min="0" value="0" step="0.01" disabled> | 
					
					
						
						| 
							 | 
						                    <button class="time-set-button" id="set-start-time" disabled>現在の秒数に設定</button> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="setting-item"> | 
					
					
						
						| 
							 | 
						                <label for="end-time">再生終了秒数:</label> | 
					
					
						
						| 
							 | 
						                <div class="time-input-container"> | 
					
					
						
						| 
							 | 
						                    <input type="number" id="end-time" min="0" value="0" step="0.01" disabled> | 
					
					
						
						| 
							 | 
						                    <button class="time-set-button" id="set-end-time" disabled>現在の秒数に設定</button> | 
					
					
						
						| 
							 | 
						                    <button class="time-set-button" id="reset-end-time" disabled>音声の長さに戻す</button> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="setting-item"> | 
					
					
						
						| 
							 | 
						                <label for="loop">ループ再生:</label> | 
					
					
						
						| 
							 | 
						                <input type="checkbox" id="loop" disabled> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="setting-item"> | 
					
					
						
						| 
							 | 
						                <button class="combine-button" id="apply-time-btn" disabled>時間設定を適用</button> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <h2>設定</h2> | 
					
					
						
						| 
							 | 
						            <div class="setting-item"> | 
					
					
						
						| 
							 | 
						                <div class="global-volume-container"> | 
					
					
						
						| 
							 | 
						                    <label>再生音量:</label> | 
					
					
						
						| 
							 | 
						                    <input type="range" class="global-volume-slider" id="global-volume" min="0" max="1" step="0.01" value="0.5" disabled> | 
					
					
						
						| 
							 | 
						                    <span class="slider-value" id="global-volume-value">0.50</span> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="setting-item"> | 
					
					
						
						| 
							 | 
						                <div class="playback-speed-container"> | 
					
					
						
						| 
							 | 
						                    <label>再生速度:</label> | 
					
					
						
						| 
							 | 
						                    <input type="range" class="playback-speed-slider" id="playback-speed" min="0.01" max="5" step="0.001" value="1" disabled> | 
					
					
						
						| 
							 | 
						                    <span class="slider-value" id="playback-speed-value">1.00x</span> | 
					
					
						
						| 
							 | 
						                </div> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <div class="setting-item"> | 
					
					
						
						| 
							 | 
						                <label for="tempo">テンポ (BPM):</label> | 
					
					
						
						| 
							 | 
						                <input type="number" id="tempo" min="40" max="200" value="92" step="0.1"> | 
					
					
						
						| 
							 | 
						                <span id="tempo-speed-value">1.00x</span> | 
					
					
						
						| 
							 | 
						            </div> | 
					
					
						
						| 
							 | 
						            <span>↑練習したいテンポを入力するとそのテンポに合わせて再生速度スライダーを変更できます。 | 
					
					
						
						| 
							 | 
						                <br>楽譜に記載された「♪=」の横のテンポ(時の旅人は約92、地球星歌は約66)を基準に計算します。</span> | 
					
					
						
						| 
							 | 
						        </div> | 
					
					
						
						| 
							 | 
						    </div> | 
					
					
						
						| 
							 | 
						    <script> | 
					
					
						
						| 
							 | 
						        window.addEventListener('load', async () => { | 
					
					
						
						| 
							 | 
						            const statusElem = document.getElementById('sw-status'); | 
					
					
						
						| 
							 | 
						            const registerBtn = document.getElementById('sw-register-btn'); | 
					
					
						
						| 
							 | 
						            const UPDATE_URL = '/update.txt'; | 
					
					
						
						| 
							 | 
						            const LS_SW_VERSION = 'sw_version'; | 
					
					
						
						| 
							 | 
						            const LS_LAST_SEEN = 'last_seen_update_txt'; | 
					
					
						
						| 
							 | 
						            const CHECK_INTERVAL_MS = 30000; | 
					
					
						
						| 
							 | 
						            const SKIP_WAITING_TIMEOUT_MS = 8000; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const log = (v) => { console.log(v); try { if(statusElem) statusElem.textContent = String(v); } catch (e) {} }; | 
					
					
						
						| 
							 | 
						            const err = (m, e) => { console.error(m, e); try { if(statusElem) statusElem.textContent = m + (e?.message ? (' — ' + e.message) : ''); } catch (_) {} }; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if(!('serviceWorker' in navigator)) { | 
					
					
						
						| 
							 | 
						                err('このブラウザは Service Worker に対応していません。'); | 
					
					
						
						| 
							 | 
						                if(registerBtn) registerBtn.disabled = true; | 
					
					
						
						| 
							 | 
						                return; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						            if(registerBtn) registerBtn.disabled = false; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if(!document.getElementById('sw-update-style')) { | 
					
					
						
						| 
							 | 
						                const style = document.createElement('style'); | 
					
					
						
						| 
							 | 
						                style.id = 'sw-update-style'; | 
					
					
						
						| 
							 | 
						                style.textContent = `.sw-popup{position:fixed;top:20px;right:20px;background:#fff3cd;color:#856304;border:1px solid #ffeeba;border-radius:8px;padding:16px;z-index:10000;width:320px;font-family:sans-serif} | 
					
					
						
						| 
							 | 
						.sw-popup .close{position:absolute;top:6px;right:8px;background:none;border:none;font-weight:bold;cursor:pointer} | 
					
					
						
						| 
							 | 
						.sw-popup .primary{display:block;margin-top:12px;padding:8px;border:none;background:#ffc107;border-radius:4px;width:100%;cursor:pointer} | 
					
					
						
						| 
							 | 
						.sw-popup .secondary{display:block;margin-top:8px;padding:6px;border:1px solid #ddd;background:#fff;border-radius:4px;width:100%;cursor:pointer}`; | 
					
					
						
						| 
							 | 
						                document.head.appendChild(style); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const queue = []; | 
					
					
						
						| 
							 | 
						            let showing = false; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            function enqueue(type, payload = {}) { queue.push({ type, payload }); | 
					
					
						
						| 
							 | 
						                showNext(); } | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            function showNext() { if(showing || !queue.length) return; const it = queue.shift(); | 
					
					
						
						| 
							 | 
						                showing = true; | 
					
					
						
						| 
							 | 
						                show(it.type, it.payload).finally(() => { showing = false; | 
					
					
						
						| 
							 | 
						                    setTimeout(showNext, 200); }); } | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            function show(type, payload) { | 
					
					
						
						| 
							 | 
						                return new Promise(resolve => { | 
					
					
						
						| 
							 | 
						                    const pop = document.createElement('div'); | 
					
					
						
						| 
							 | 
						                    pop.className = 'sw-popup'; | 
					
					
						
						| 
							 | 
						                    pop.setAttribute('data-type', type); | 
					
					
						
						| 
							 | 
						                    let title = '更新', | 
					
					
						
						| 
							 | 
						                        body = '更新があります。'; | 
					
					
						
						| 
							 | 
						                    if(type === 'reload') { title = 'ページ更新が必要です'; | 
					
					
						
						| 
							 | 
						                        body = 'ページが新しくなりました。再読み込みしてください。'; } else if(type === 'sw-update') { title = 'Service Worker の更新'; | 
					
					
						
						| 
							 | 
						                        body = '新しい Service Worker が利用可能です。反映するには更新してください。'; } | 
					
					
						
						| 
							 | 
						                    pop.innerHTML = `<button class="close" aria-label="閉じる">×</button><strong>${title}</strong><p style="margin:10px 0 0">${body}</p><div><button class="primary">${payload.primaryText|| (type==='sw-update'?'サービスワーカーを更新する':'再読み込み')}</button><button class="secondary">${payload.secondaryText||'後で'}</button></div>`; | 
					
					
						
						| 
							 | 
						                    document.body.appendChild(pop); | 
					
					
						
						| 
							 | 
						                    pop.querySelector('.close').addEventListener('click', () => { pop.remove(); | 
					
					
						
						| 
							 | 
						                        resolve(); }); | 
					
					
						
						| 
							 | 
						                    pop.querySelector('.secondary').addEventListener('click', () => { pop.remove(); | 
					
					
						
						| 
							 | 
						                        resolve(); }); | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    const primary = pop.querySelector('.primary'); | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    if(type === 'reload') { | 
					
					
						
						| 
							 | 
						                        primary.addEventListener('click', () => location.reload()); | 
					
					
						
						| 
							 | 
						                    } else if(type === 'sw-update') { | 
					
					
						
						| 
							 | 
						                        primary.addEventListener('click', async () => { | 
					
					
						
						| 
							 | 
						                            primary.disabled = true; | 
					
					
						
						| 
							 | 
						                            try { | 
					
					
						
						| 
							 | 
						                                log('Service Worker 更新処理を開始します...'); | 
					
					
						
						| 
							 | 
						                                const reg = await navigator.serviceWorker.getRegistration(); | 
					
					
						
						| 
							 | 
						                                 | 
					
					
						
						| 
							 | 
						                                if(reg) { | 
					
					
						
						| 
							 | 
						                                    try { await reg.update(); | 
					
					
						
						| 
							 | 
						                                        log('reg.update() を呼び出しました。'); } catch (e) { console.warn('reg.update() エラー', e); } | 
					
					
						
						| 
							 | 
						                                } else { | 
					
					
						
						| 
							 | 
						                                    log('registration が見つかりません。'); | 
					
					
						
						| 
							 | 
						                                } | 
					
					
						
						| 
							 | 
						                                 | 
					
					
						
						| 
							 | 
						                                 | 
					
					
						
						| 
							 | 
						                                let handled = false; | 
					
					
						
						| 
							 | 
						                                const attemptHandle = async () => { | 
					
					
						
						| 
							 | 
						                                    const r = await navigator.serviceWorker.getRegistration(); | 
					
					
						
						| 
							 | 
						                                    if(!r) return; | 
					
					
						
						| 
							 | 
						                                    if(r.waiting) { | 
					
					
						
						| 
							 | 
						                                        log('waiting を発見: skipWaiting を送信します。'); | 
					
					
						
						| 
							 | 
						                                        r.waiting.postMessage({ type: 'SKIP_WAITING' }); | 
					
					
						
						| 
							 | 
						                                        handled = true; | 
					
					
						
						| 
							 | 
						                                         | 
					
					
						
						| 
							 | 
						                                        navigator.serviceWorker.addEventListener('controllerchange', function oncc() { | 
					
					
						
						| 
							 | 
						                                            navigator.serviceWorker.removeEventListener('controllerchange', oncc); | 
					
					
						
						| 
							 | 
						                                            (async () => { | 
					
					
						
						| 
							 | 
						                                                 | 
					
					
						
						| 
							 | 
						                                                const latest = await fetchVersion(); | 
					
					
						
						| 
							 | 
						                                                if(latest) { | 
					
					
						
						| 
							 | 
						                                                    localStorage.setItem(LS_SW_VERSION, latest); | 
					
					
						
						| 
							 | 
						                                                    log('sw_version を保存しました: ' + latest); | 
					
					
						
						| 
							 | 
						                                                } | 
					
					
						
						| 
							 | 
						                                                setTimeout(() => location.reload(), 400); | 
					
					
						
						| 
							 | 
						                                            })(); | 
					
					
						
						| 
							 | 
						                                        }); | 
					
					
						
						| 
							 | 
						                                        return; | 
					
					
						
						| 
							 | 
						                                    } | 
					
					
						
						| 
							 | 
						                                    if(r.installing) { | 
					
					
						
						| 
							 | 
						                                        log('installing を監視します...'); | 
					
					
						
						| 
							 | 
						                                        r.installing.addEventListener('statechange', function onsc() { | 
					
					
						
						| 
							 | 
						                                            if(r.installing.state === 'installed') { | 
					
					
						
						| 
							 | 
						                                                r.installing.removeEventListener('statechange', onsc); | 
					
					
						
						| 
							 | 
						                                                if(r.waiting) { | 
					
					
						
						| 
							 | 
						                                                    r.waiting.postMessage({ type: 'SKIP_WAITING' }); | 
					
					
						
						| 
							 | 
						                                                } | 
					
					
						
						| 
							 | 
						                                            } | 
					
					
						
						| 
							 | 
						                                        }); | 
					
					
						
						| 
							 | 
						                                        return; | 
					
					
						
						| 
							 | 
						                                    } | 
					
					
						
						| 
							 | 
						                                }; | 
					
					
						
						| 
							 | 
						                                 | 
					
					
						
						| 
							 | 
						                                await attemptHandle(); | 
					
					
						
						| 
							 | 
						                                 | 
					
					
						
						| 
							 | 
						                                 | 
					
					
						
						| 
							 | 
						                                setTimeout(async () => { | 
					
					
						
						| 
							 | 
						                                    if(!handled) { | 
					
					
						
						| 
							 | 
						                                        log('waiting の応答がありません。フォールバックで update.txt を保存してリロードします。'); | 
					
					
						
						| 
							 | 
						                                        const latest = await fetchVersion(); | 
					
					
						
						| 
							 | 
						                                        if(latest) { | 
					
					
						
						| 
							 | 
						                                            localStorage.setItem(LS_SW_VERSION, latest); | 
					
					
						
						| 
							 | 
						                                            log('sw_version を保存しました(フォールバック): ' + latest); | 
					
					
						
						| 
							 | 
						                                        } | 
					
					
						
						| 
							 | 
						                                        location.reload(); | 
					
					
						
						| 
							 | 
						                                    } | 
					
					
						
						| 
							 | 
						                                }, SKIP_WAITING_TIMEOUT_MS); | 
					
					
						
						| 
							 | 
						                                 | 
					
					
						
						| 
							 | 
						                            } catch (e) { | 
					
					
						
						| 
							 | 
						                                err('sw-update 処理でエラー', e); | 
					
					
						
						| 
							 | 
						                                const latest = await fetchVersion(); | 
					
					
						
						| 
							 | 
						                                if(latest) localStorage.setItem(LS_SW_VERSION, latest); | 
					
					
						
						| 
							 | 
						                                location.reload(); | 
					
					
						
						| 
							 | 
						                            } | 
					
					
						
						| 
							 | 
						                        }); | 
					
					
						
						| 
							 | 
						                    } else { | 
					
					
						
						| 
							 | 
						                        primary.addEventListener('click', () => { pop.remove(); | 
					
					
						
						| 
							 | 
						                            resolve(); }); | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            async function fetchVersion() { | 
					
					
						
						| 
							 | 
						                try { | 
					
					
						
						| 
							 | 
						                    const r = await fetch(UPDATE_URL + '?t=' + Date.now(), { cache: 'no-store' }); | 
					
					
						
						| 
							 | 
						                    if(r.ok) return (await r.text()).trim(); | 
					
					
						
						| 
							 | 
						                } catch (e) { console.warn('update.txt fetch failed', e); } | 
					
					
						
						| 
							 | 
						                return null; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            let lastSeen = localStorage.getItem(LS_LAST_SEEN) || null; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            async function check() { | 
					
					
						
						| 
							 | 
						                const latest = await fetchVersion(); | 
					
					
						
						| 
							 | 
						                if(!latest) return; | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                if(lastSeen && latest !== lastSeen) { | 
					
					
						
						| 
							 | 
						                    enqueue('reload'); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						                lastSeen = latest; | 
					
					
						
						| 
							 | 
						                localStorage.setItem(LS_LAST_SEEN, latest); | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const swv = localStorage.getItem(LS_SW_VERSION); | 
					
					
						
						| 
							 | 
						                if(swv && swv !== latest) { | 
					
					
						
						| 
							 | 
						                    enqueue('sw-update'); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            (async () => { | 
					
					
						
						| 
							 | 
						                const latest = await fetchVersion(); | 
					
					
						
						| 
							 | 
						                if(latest) { | 
					
					
						
						| 
							 | 
						                    if(!lastSeen) { | 
					
					
						
						| 
							 | 
						                        lastSeen = latest; | 
					
					
						
						| 
							 | 
						                        localStorage.setItem(LS_LAST_SEEN, latest); | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                    const swv = localStorage.getItem(LS_SW_VERSION); | 
					
					
						
						| 
							 | 
						                    if(swv && swv !== latest) enqueue('sw-update'); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						                setInterval(check, CHECK_INTERVAL_MS); | 
					
					
						
						| 
							 | 
						            })(); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if(registerBtn) { | 
					
					
						
						| 
							 | 
						                registerBtn.addEventListener('click', async () => { | 
					
					
						
						| 
							 | 
						                    registerBtn.disabled = true; | 
					
					
						
						| 
							 | 
						                    try { | 
					
					
						
						| 
							 | 
						                        log('既存の Service Worker を unregister...'); | 
					
					
						
						| 
							 | 
						                        const regs = await navigator.serviceWorker.getRegistrations(); | 
					
					
						
						| 
							 | 
						                        for(const r of regs) await r.unregister(); | 
					
					
						
						| 
							 | 
						                        log('registering...'); | 
					
					
						
						| 
							 | 
						                        await navigator.serviceWorker.register('/sw.js'); | 
					
					
						
						| 
							 | 
						                        const newv = await fetchVersion(); | 
					
					
						
						| 
							 | 
						                        if(newv) { | 
					
					
						
						| 
							 | 
						                            localStorage.setItem(LS_SW_VERSION, newv); | 
					
					
						
						| 
							 | 
						                            log('sw_version を保存: ' + newv); | 
					
					
						
						| 
							 | 
						                        } else { | 
					
					
						
						| 
							 | 
						                            log('update.txt を取得できませんでした(sw_version 保存不可)。'); | 
					
					
						
						| 
							 | 
						                        } | 
					
					
						
						| 
							 | 
						                        registerBtn.textContent = 'ページをリロード'; | 
					
					
						
						| 
							 | 
						                        registerBtn.disabled = false; | 
					
					
						
						| 
							 | 
						                        registerBtn.onclick = () => location.reload(); | 
					
					
						
						| 
							 | 
						                    } catch (e) { | 
					
					
						
						| 
							 | 
						                        err('登録失敗', e); | 
					
					
						
						| 
							 | 
						                        registerBtn.disabled = false; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						    </script> | 
					
					
						
						| 
							 | 
						<script> | 
					
					
						
						| 
							 | 
						    document.addEventListener('DOMContentLoaded', function() { | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        let lastSyncTime = 0; | 
					
					
						
						| 
							 | 
						        let isBuffering = false; | 
					
					
						
						| 
							 | 
						        let syncDriftLog = []; | 
					
					
						
						| 
							 | 
						        let syncCheckInterval; | 
					
					
						
						| 
							 | 
						        let audioContext; | 
					
					
						
						| 
							 | 
						        let isCheckingSync = false; | 
					
					
						
						| 
							 | 
						        let isInBackgroundTab = false; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        try { | 
					
					
						
						| 
							 | 
						            audioContext = new(window.AudioContext || window.webkitAudioContext)(); | 
					
					
						
						| 
							 | 
						        } catch (e) { | 
					
					
						
						| 
							 | 
						            console.error('Web Audio APIがサポートされていません:', e); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        const urlParams = new URLSearchParams(window.location.search); | 
					
					
						
						| 
							 | 
						        const isTMode = urlParams.has('mode') && urlParams.get('mode') === 't'; | 
					
					
						
						| 
							 | 
						        const basePath = isTMode ? '/t/' : '/'; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        let loadingCount = 0; | 
					
					
						
						| 
							 | 
						        let totalToLoad = 5;  | 
					
					
						
						| 
							 | 
						        let lastUpdateTime = 0; | 
					
					
						
						| 
							 | 
						        const updateInterval = 100; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function checkLoadingComplete() { | 
					
					
						
						| 
							 | 
						            loadingCount++; | 
					
					
						
						| 
							 | 
						            if (loadingCount >= totalToLoad) { | 
					
					
						
						| 
							 | 
						                setTimeout(function() { | 
					
					
						
						| 
							 | 
						                    const loadingOverlay = document.getElementById('loadingOverlay'); | 
					
					
						
						| 
							 | 
						                    if (loadingOverlay) { | 
					
					
						
						| 
							 | 
						                        loadingOverlay.style.opacity = '0'; | 
					
					
						
						| 
							 | 
						                        setTimeout(function() { | 
					
					
						
						| 
							 | 
						                            loadingOverlay.style.display = 'none'; | 
					
					
						
						| 
							 | 
						                        }, 1000); | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                }, 500); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function handleError(error, message) { | 
					
					
						
						| 
							 | 
						            console.error(message, error); | 
					
					
						
						| 
							 | 
						            window.alert(`${message}\n\nエラー詳細: ${error.message}`); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const playPauseBtn = document.getElementById('play-pause-btn'); | 
					
					
						
						| 
							 | 
						        const timeDisplay = document.getElementById('time-display'); | 
					
					
						
						| 
							 | 
						        const progressContainer = document.getElementById('progress-container'); | 
					
					
						
						| 
							 | 
						        const progressBar = document.getElementById('progress-bar'); | 
					
					
						
						| 
							 | 
						        const progressTime = document.getElementById('progress-time'); | 
					
					
						
						| 
							 | 
						        const volumeBtn = document.getElementById('volume-btn'); | 
					
					
						
						| 
							 | 
						        const volumeSlider = document.getElementById('volume-slider'); | 
					
					
						
						| 
							 | 
						        const speedSlider = document.getElementById('speed-slider'); | 
					
					
						
						| 
							 | 
						        const speedValue = document.getElementById('speed-value'); | 
					
					
						
						| 
							 | 
						        const playbackSpeedSlider = document.getElementById('playback-speed'); | 
					
					
						
						| 
							 | 
						        const playbackSpeedValue = document.getElementById('playback-speed-value'); | 
					
					
						
						| 
							 | 
						        const startTimeInput = document.getElementById('start-time'); | 
					
					
						
						| 
							 | 
						        const endTimeInput = document.getElementById('end-time'); | 
					
					
						
						| 
							 | 
						        const loopCheckbox = document.getElementById('loop'); | 
					
					
						
						| 
							 | 
						        const globalVolumeSlider = document.getElementById('global-volume'); | 
					
					
						
						| 
							 | 
						        const globalVolumeValue = document.getElementById('global-volume-value'); | 
					
					
						
						| 
							 | 
						        const audioSliders = document.querySelectorAll('.audio-slider'); | 
					
					
						
						| 
							 | 
						        const volumeValues = document.querySelectorAll('.volume-value'); | 
					
					
						
						| 
							 | 
						        const setStartTimeBtn = document.getElementById('set-start-time'); | 
					
					
						
						| 
							 | 
						        const setEndTimeBtn = document.getElementById('set-end-time'); | 
					
					
						
						| 
							 | 
						        const resetEndTimeBtn = document.getElementById('reset-end-time'); | 
					
					
						
						| 
							 | 
						        const combineButton = document.getElementById('combine-button'); | 
					
					
						
						| 
							 | 
						        const combineStatus = document.getElementById('combine-status'); | 
					
					
						
						| 
							 | 
						        const previewSection = document.getElementById('preview-section'); | 
					
					
						
						| 
							 | 
						        const previewButton = document.getElementById('preview-button'); | 
					
					
						
						| 
							 | 
						        const previewTime = document.getElementById('preview-time'); | 
					
					
						
						| 
							 | 
						        const syncStatus = document.getElementById('sync-status'); | 
					
					
						
						| 
							 | 
						        const syncStatusText = document.getElementById('sync-status-text'); | 
					
					
						
						| 
							 | 
						        const syncStatusClose = document.getElementById('sync-status-close'); | 
					
					
						
						| 
							 | 
						        const startMarker = document.getElementById('start-marker'); | 
					
					
						
						| 
							 | 
						        const endMarker = document.getElementById('end-marker'); | 
					
					
						
						| 
							 | 
						        const tempoInput = document.getElementById('tempo'); | 
					
					
						
						| 
							 | 
						        const tempoSpeedValue = document.getElementById('tempo-speed-value'); | 
					
					
						
						| 
							 | 
						        const applyTimeBtn = document.getElementById('apply-time-btn'); | 
					
					
						
						| 
							 | 
						        const resetBtn = document.getElementById('reset-btn'); | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const ffmpegVolumeSlider = document.getElementById('ffmpeg-volume'); | 
					
					
						
						| 
							 | 
						        const ffmpegVolumeValue = document.getElementById('ffmpeg-volume-value'); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const audioElements = {}; | 
					
					
						
						| 
							 | 
						        const audioBuffers = {}; | 
					
					
						
						| 
							 | 
						        const audioFiles = ['p', 'a', 't', 's', 'k']; | 
					
					
						
						| 
							 | 
						        let combinedAudioElement = null; | 
					
					
						
						| 
							 | 
						        let isAudioCombined = false; | 
					
					
						
						| 
							 | 
						        let currentVolumes = { | 
					
					
						
						| 
							 | 
						            p: 0, | 
					
					
						
						| 
							 | 
						            a: 1, | 
					
					
						
						| 
							 | 
						            t: 1, | 
					
					
						
						| 
							 | 
						            s: 1, | 
					
					
						
						| 
							 | 
						            k: 0 | 
					
					
						
						| 
							 | 
						        }; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        let audioDuration = 0; | 
					
					
						
						| 
							 | 
						        let isPlaying = false; | 
					
					
						
						| 
							 | 
						        let lastVolume = 1; | 
					
					
						
						| 
							 | 
						        let currentPlaybackRate = 1; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (resetBtn) { | 
					
					
						
						| 
							 | 
						            resetBtn.addEventListener('click', () => { | 
					
					
						
						| 
							 | 
						                const startTime = parseFloat(startTimeInput.value) || 0; | 
					
					
						
						| 
							 | 
						                seekMedia(startTime); | 
					
					
						
						| 
							 | 
						                if (isPlaying) { | 
					
					
						
						| 
							 | 
						                    playMedia(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function startSyncCheck() { | 
					
					
						
						| 
							 | 
						            if (isCheckingSync) return; | 
					
					
						
						| 
							 | 
						            isCheckingSync = true; | 
					
					
						
						| 
							 | 
						            if (syncCheckInterval) clearInterval(syncCheckInterval); | 
					
					
						
						| 
							 | 
						            syncCheckInterval = setInterval(checkSync, 1000); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function stopSyncCheck() { | 
					
					
						
						| 
							 | 
						            isCheckingSync = false; | 
					
					
						
						| 
							 | 
						            if (syncCheckInterval) clearInterval(syncCheckInterval); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function checkSync() { | 
					
					
						
						| 
							 | 
						            if (!isAudioCombined || !isPlaying || isBuffering || isInBackgroundTab) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            const audioTime = combinedAudioElement.currentTime; | 
					
					
						
						| 
							 | 
						            const drift = audioTime - audioTime;  | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            syncDriftLog.push(drift); | 
					
					
						
						| 
							 | 
						            if (syncDriftLog.length > 5) syncDriftLog.shift(); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            const avgDrift = syncDriftLog.reduce((a, b) => a + b, 0) / syncDriftLog.length; | 
					
					
						
						| 
							 | 
						            if (syncStatusText) { | 
					
					
						
						| 
							 | 
						                syncStatusText.textContent = `同期ズレ: ${avgDrift.toFixed(3)}秒`; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        if (applyTimeBtn) { | 
					
					
						
						| 
							 | 
						            applyTimeBtn.addEventListener('click', function() { | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const wasPlaying = isPlaying; | 
					
					
						
						| 
							 | 
						                if (isPlaying) { | 
					
					
						
						| 
							 | 
						                    pauseMedia(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const startTime = parseFloat(startTimeInput.value) || 0; | 
					
					
						
						| 
							 | 
						                const endTime = parseFloat(endTimeInput.value) || audioDuration; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                if (combinedAudioElement.currentTime < startTime) { | 
					
					
						
						| 
							 | 
						                    combinedAudioElement.currentTime = startTime; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                else if (combinedAudioElement.currentTime > endTime) { | 
					
					
						
						| 
							 | 
						                    combinedAudioElement.currentTime = startTime; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                if (wasPlaying) { | 
					
					
						
						| 
							 | 
						                    playMedia(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                updateProgressMarkers(); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function loadAudioFiles() { | 
					
					
						
						| 
							 | 
						            audioFiles.forEach(file => { | 
					
					
						
						| 
							 | 
						                try { | 
					
					
						
						| 
							 | 
						                    const audio = new Audio(`${basePath}${file}.mp3`); | 
					
					
						
						| 
							 | 
						                    audio.preload = 'auto'; | 
					
					
						
						| 
							 | 
						                    audio.loop = false; | 
					
					
						
						| 
							 | 
						                    audioElements[file] = audio; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    audio.addEventListener('loadedmetadata', function() { | 
					
					
						
						| 
							 | 
						                        console.log(`${basePath}${file}.mp3 loaded`); | 
					
					
						
						| 
							 | 
						                        checkLoadingComplete(); | 
					
					
						
						| 
							 | 
						                    }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    audio.addEventListener('error', function() { | 
					
					
						
						| 
							 | 
						                        console.error(`音声ファイル読み込みエラー (${basePath}${file}.mp3):`, audio.error); | 
					
					
						
						| 
							 | 
						                        checkLoadingComplete(); | 
					
					
						
						| 
							 | 
						                    }); | 
					
					
						
						| 
							 | 
						                } catch (error) { | 
					
					
						
						| 
							 | 
						                    console.error(`音声ファイル初期化エラー (${basePath}${file}.mp3):`, error); | 
					
					
						
						| 
							 | 
						                    checkLoadingComplete(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        async function combineAudio() { | 
					
					
						
						| 
							 | 
						            if (combinedAudioElement) { | 
					
					
						
						| 
							 | 
						                combinedAudioElement.pause(); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const oldSrc = combinedAudioElement.src; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                combinedAudioElement.src = ''; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                if (oldSrc.startsWith('blob:')) { | 
					
					
						
						| 
							 | 
						                    URL.revokeObjectURL(oldSrc); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						            combineButton.disabled = true; | 
					
					
						
						| 
							 | 
						            combineStatus.textContent = "音声を合成中..."; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            try { | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                audioFiles.forEach(file => { | 
					
					
						
						| 
							 | 
						                    currentVolumes[file] = parseFloat(document.querySelector(`.audio-slider[data-audio="${file}"]`).value); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const ffmpegVolume = parseFloat(ffmpegVolumeSlider.value); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const audioBufferPromises = audioFiles.map(async file => { | 
					
					
						
						| 
							 | 
						                    const audio = audioElements[file]; | 
					
					
						
						| 
							 | 
						                    if (!audio) return null; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    if (currentVolumes[file] === 0) return null; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    const response = await fetch(`${basePath}${file}.mp3`); | 
					
					
						
						| 
							 | 
						                    const arrayBuffer = await response.arrayBuffer(); | 
					
					
						
						| 
							 | 
						                    return await audioContext.decodeAudioData(arrayBuffer); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const buffers = await Promise.all(audioBufferPromises); | 
					
					
						
						| 
							 | 
						                audioFiles.forEach((file, index) => { | 
					
					
						
						| 
							 | 
						                    audioBuffers[file] = buffers[index]; | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const maxDuration = Math.max(...buffers.filter(b => b).map(b => b.duration)); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const combinedAudioBuffer = audioContext.createBuffer( | 
					
					
						
						| 
							 | 
						                    2,  | 
					
					
						
						| 
							 | 
						                    audioContext.sampleRate * maxDuration, | 
					
					
						
						| 
							 | 
						                    audioContext.sampleRate | 
					
					
						
						| 
							 | 
						                ); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                for (let file of audioFiles) { | 
					
					
						
						| 
							 | 
						                    if (!audioBuffers[file] || currentVolumes[file] === 0) continue; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    const buffer = audioBuffers[file]; | 
					
					
						
						| 
							 | 
						                    const volume = currentVolumes[file] * ffmpegVolume;  | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    for (let channel = 0; channel < 2; channel++) { | 
					
					
						
						| 
							 | 
						                        const inputData = buffer.getChannelData(channel % buffer.numberOfChannels); | 
					
					
						
						| 
							 | 
						                        const outputData = combinedAudioBuffer.getChannelData(channel); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                        for (let i = 0; i < inputData.length; i++) { | 
					
					
						
						| 
							 | 
						                            outputData[i] += inputData[i] * volume; | 
					
					
						
						| 
							 | 
						                        } | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const blob = bufferToWave(combinedAudioBuffer); | 
					
					
						
						| 
							 | 
						                const url = URL.createObjectURL(blob); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                combinedAudioElement = new Audio(url); | 
					
					
						
						| 
							 | 
						                combinedAudioElement.preservesPitch = true; | 
					
					
						
						| 
							 | 
						                combinedAudioElement.mozPreservesPitch = true; | 
					
					
						
						| 
							 | 
						                combinedAudioElement.webkitPreservesPitch = true; | 
					
					
						
						| 
							 | 
						                combinedAudioElement.playbackRate = currentPlaybackRate; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                combinedAudioElement.addEventListener('loadedmetadata', function() { | 
					
					
						
						| 
							 | 
						                    try { | 
					
					
						
						| 
							 | 
						                        audioDuration = combinedAudioElement.duration; | 
					
					
						
						| 
							 | 
						                        if (endTimeInput) { | 
					
					
						
						| 
							 | 
						                            endTimeInput.value = audioDuration.toFixed(2); | 
					
					
						
						| 
							 | 
						                            endTimeInput.max = audioDuration; | 
					
					
						
						| 
							 | 
						                        } | 
					
					
						
						| 
							 | 
						                        if (startTimeInput) { | 
					
					
						
						| 
							 | 
						                            startTimeInput.max = audioDuration - 0.1; | 
					
					
						
						| 
							 | 
						                        } | 
					
					
						
						| 
							 | 
						                        updateTimeDisplay(); | 
					
					
						
						| 
							 | 
						                    } catch (error) { | 
					
					
						
						| 
							 | 
						                        handleError(error, '音声メタデータ読み込み中にエラーが発生しました'); | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                combinedAudioElement.addEventListener('error', function() { | 
					
					
						
						| 
							 | 
						                    handleError(combinedAudioElement.error, '音声読み込み中にエラーが発生しました'); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                combinedAudioElement.addEventListener('timeupdate', function() { | 
					
					
						
						| 
							 | 
						                    updateTimeDisplay(); | 
					
					
						
						| 
							 | 
						                    checkEndTime(); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                combinedAudioElement.addEventListener('ended', function() { | 
					
					
						
						| 
							 | 
						                    handleAudioEnd(); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                isAudioCombined = true; | 
					
					
						
						| 
							 | 
						                combineStatus.textContent = "音声の合成が完了しました"; | 
					
					
						
						| 
							 | 
						                enablePlayerControls(); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                combineButton.disabled = false; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                document.addEventListener('visibilitychange', async () => { | 
					
					
						
						| 
							 | 
						                    if (document.hidden) { | 
					
					
						
						| 
							 | 
						                        isInBackgroundTab = true; | 
					
					
						
						| 
							 | 
						                    } else { | 
					
					
						
						| 
							 | 
						                        isInBackgroundTab = false; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                applyVolume(); | 
					
					
						
						| 
							 | 
						                applyPlaybackRate(); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            } catch (error) { | 
					
					
						
						| 
							 | 
						                console.error('音声合成エラー:', error); | 
					
					
						
						| 
							 | 
						                combineStatus.textContent = "音声の合成に失敗しました"; | 
					
					
						
						| 
							 | 
						                combineButton.disabled = false; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function checkEndTime() { | 
					
					
						
						| 
							 | 
						            if (!combinedAudioElement || !isAudioCombined) return; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const currentTime = combinedAudioElement.currentTime; | 
					
					
						
						| 
							 | 
						            const endTime = parseFloat(endTimeInput.value) || combinedAudioElement.duration; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if (currentTime >= endTime) { | 
					
					
						
						| 
							 | 
						                if (loopCheckbox && loopCheckbox.checked) { | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    const startTime = parseFloat(startTimeInput.value) || 0; | 
					
					
						
						| 
							 | 
						                    seekMedia(startTime); | 
					
					
						
						| 
							 | 
						                    if (isPlaying) { | 
					
					
						
						| 
							 | 
						                        playMedia(); | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                } else { | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    pauseMedia(); | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    combinedAudioElement.currentTime = endTime; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function handleAudioEnd() { | 
					
					
						
						| 
							 | 
						            if (loopCheckbox && loopCheckbox.checked) { | 
					
					
						
						| 
							 | 
						                const startTime = parseFloat(startTimeInput.value) || 0; | 
					
					
						
						| 
							 | 
						                const endTime = parseFloat(endTimeInput.value) || combinedAudioElement.duration; | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                seekMedia(startTime); | 
					
					
						
						| 
							 | 
						                if (isPlaying) { | 
					
					
						
						| 
							 | 
						                    playMedia(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } else { | 
					
					
						
						| 
							 | 
						                pauseMedia(); | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                const endTime = parseFloat(endTimeInput.value) || combinedAudioElement.duration; | 
					
					
						
						| 
							 | 
						                combinedAudioElement.currentTime = endTime; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function bufferToWave(abuffer) { | 
					
					
						
						| 
							 | 
						            const numOfChan = abuffer.numberOfChannels, | 
					
					
						
						| 
							 | 
						                length = abuffer.length * numOfChan * 2 + 44, | 
					
					
						
						| 
							 | 
						                buffer = new ArrayBuffer(length), | 
					
					
						
						| 
							 | 
						                view = new DataView(buffer), | 
					
					
						
						| 
							 | 
						                channels = [], | 
					
					
						
						| 
							 | 
						                sampleRate = abuffer.sampleRate; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            let pos = 0; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            setUint32(0x46464952);  | 
					
					
						
						| 
							 | 
						            setUint32(length - 8);  | 
					
					
						
						| 
							 | 
						            setUint32(0x45564157);  | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            setUint32(0x20746d66);  | 
					
					
						
						| 
							 | 
						            setUint32(16);  | 
					
					
						
						| 
							 | 
						            setUint16(1);  | 
					
					
						
						| 
							 | 
						            setUint16(numOfChan); | 
					
					
						
						| 
							 | 
						            setUint32(sampleRate); | 
					
					
						
						| 
							 | 
						            setUint32(sampleRate * 2 * numOfChan); | 
					
					
						
						| 
							 | 
						            setUint16(numOfChan * 2); | 
					
					
						
						| 
							 | 
						            setUint16(16); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            setUint32(0x61746164);  | 
					
					
						
						| 
							 | 
						            setUint32(length - pos - 4); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            for (let i = 0; i < abuffer.length; i++) { | 
					
					
						
						| 
							 | 
						                for (let channel = 0; channel < numOfChan; channel++) { | 
					
					
						
						| 
							 | 
						                    let sample = abuffer.getChannelData(channel)[i] * 0x7fff; | 
					
					
						
						| 
							 | 
						                    if (sample < -32768) sample = -32768; | 
					
					
						
						| 
							 | 
						                    if (sample > 32767) sample = 32767; | 
					
					
						
						| 
							 | 
						                    view.setInt16(pos, sample, true); | 
					
					
						
						| 
							 | 
						                    pos += 2; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            function setUint16(data) { | 
					
					
						
						| 
							 | 
						                view.setUint16(pos, data, true); | 
					
					
						
						| 
							 | 
						                pos += 2; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            function setUint32(data) { | 
					
					
						
						| 
							 | 
						                view.setUint32(pos, data, true); | 
					
					
						
						| 
							 | 
						                pos += 4; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            return new Blob([buffer], { | 
					
					
						
						| 
							 | 
						                type: 'audio/wav' | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function applyVolume() { | 
					
					
						
						| 
							 | 
						            if (!isAudioCombined || !combinedAudioElement) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const baseVolume = parseFloat(volumeSlider.value); | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const globalVolume = parseFloat(globalVolumeSlider.value); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const finalVolume = Math.max(0, Math.min(1, baseVolume * globalVolume)); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            combinedAudioElement.volume = finalVolume; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            updateVolumeIcon(); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function applyPlaybackRate() { | 
					
					
						
						| 
							 | 
						            if (!isAudioCombined || !combinedAudioElement) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            const speed = parseFloat(playbackSpeedSlider.value); | 
					
					
						
						| 
							 | 
						            currentPlaybackRate = speed; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            combinedAudioElement.playbackRate = speed; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            if (speedValue) speedValue.textContent = speed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						            if (playbackSpeedValue) playbackSpeedValue.textContent = speed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						            if (speedSlider) speedSlider.value = speed; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function enablePlayerControls() { | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const disabledOverlay = document.getElementById('disabledOverlay'); | 
					
					
						
						| 
							 | 
						            if (disabledOverlay) { | 
					
					
						
						| 
							 | 
						                disabledOverlay.style.display = 'none'; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            if (playPauseBtn) playPauseBtn.disabled = false; | 
					
					
						
						| 
							 | 
						            if (volumeBtn) volumeBtn.disabled = false; | 
					
					
						
						| 
							 | 
						            if (volumeSlider) volumeSlider.disabled = false; | 
					
					
						
						| 
							 | 
						            if (speedSlider) speedSlider.disabled = false; | 
					
					
						
						| 
							 | 
						            if (startTimeInput) startTimeInput.disabled = false; | 
					
					
						
						| 
							 | 
						            if (endTimeInput) endTimeInput.disabled = false; | 
					
					
						
						| 
							 | 
						            if (resetEndTimeBtn) resetEndTimeBtn.disabled = false; | 
					
					
						
						| 
							 | 
						            if (loopCheckbox) loopCheckbox.disabled = false; | 
					
					
						
						| 
							 | 
						            if (globalVolumeSlider) globalVolumeSlider.disabled = false; | 
					
					
						
						| 
							 | 
						            if (setStartTimeBtn) setStartTimeBtn.disabled = false; | 
					
					
						
						| 
							 | 
						            if (setEndTimeBtn) setEndTimeBtn.disabled = false; | 
					
					
						
						| 
							 | 
						            if (playbackSpeedSlider) playbackSpeedSlider.disabled = false; | 
					
					
						
						| 
							 | 
						            if (applyTimeBtn) applyTimeBtn.disabled = false; | 
					
					
						
						| 
							 | 
						            if (resetBtn) resetBtn.disabled = false; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function togglePreview() { | 
					
					
						
						| 
							 | 
						            if (!isAudioCombined || !combinedAudioElement || !previewButton) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            if (previewButton.textContent === '▶') { | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                combinedAudioElement.currentTime = 0; | 
					
					
						
						| 
							 | 
						                combinedAudioElement.play() | 
					
					
						
						| 
							 | 
						                    .then(() => { | 
					
					
						
						| 
							 | 
						                        previewButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M520-200v-560h240v560H520Zm-320 0v-560h240v560H200Zm400-80h80v-400h-80v400Zm-320 0h80v-400h-80v400Zm0-400v400-400Zm320 0v400-400Z"/></svg>'; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                         | 
					
					
						
						| 
							 | 
						                        const updatePreviewTime = () => { | 
					
					
						
						| 
							 | 
						                            if (!combinedAudioElement || !isAudioCombined || !previewTime) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                            const currentTime = combinedAudioElement.currentTime; | 
					
					
						
						| 
							 | 
						                            const duration = combinedAudioElement.duration; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                            if (currentTime >= duration) { | 
					
					
						
						| 
							 | 
						                                previewButton.textContent = '▶'; | 
					
					
						
						| 
							 | 
						                                previewTime.textContent = `00:00 / ${formatTime(duration)}`; | 
					
					
						
						| 
							 | 
						                                return; | 
					
					
						
						| 
							 | 
						                            } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                            previewTime.textContent = `${formatTime(currentTime)} / ${formatTime(duration)}`; | 
					
					
						
						| 
							 | 
						                            requestAnimationFrame(updatePreviewTime); | 
					
					
						
						| 
							 | 
						                        }; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                        updatePreviewTime(); | 
					
					
						
						| 
							 | 
						                    }) | 
					
					
						
						| 
							 | 
						                    .catch(e => console.error('プレビュー再生エラー:', e)); | 
					
					
						
						| 
							 | 
						            } else { | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                combinedAudioElement.pause(); | 
					
					
						
						| 
							 | 
						                previewButton.textContent = '▶'; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function formatTime(seconds) { | 
					
					
						
						| 
							 | 
						            const mins = Math.floor(seconds / 60); | 
					
					
						
						| 
							 | 
						            const secs = Math.floor(seconds % 60); | 
					
					
						
						| 
							 | 
						            return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function updateTimeDisplay() { | 
					
					
						
						| 
							 | 
						            const now = performance.now(); | 
					
					
						
						| 
							 | 
						            if (now - lastUpdateTime < updateInterval) return; | 
					
					
						
						| 
							 | 
						            lastUpdateTime = now; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            try { | 
					
					
						
						| 
							 | 
						                if (!combinedAudioElement || !timeDisplay) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const currentTime = combinedAudioElement.currentTime; | 
					
					
						
						| 
							 | 
						                const duration = combinedAudioElement.duration || audioDuration; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const currentMinutes = Math.floor(currentTime / 60); | 
					
					
						
						| 
							 | 
						                const currentSeconds = Math.floor(currentTime % 60); | 
					
					
						
						| 
							 | 
						                const currentMilliseconds = Math.floor((currentTime % 1) * 100); | 
					
					
						
						| 
							 | 
						                const durationMinutes = Math.floor(duration / 60); | 
					
					
						
						| 
							 | 
						                const durationSeconds = Math.floor(duration % 60); | 
					
					
						
						| 
							 | 
						                const durationMilliseconds = Math.floor((duration % 1) * 100); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                timeDisplay.textContent = | 
					
					
						
						| 
							 | 
						                    `${String(currentMinutes).padStart(2, '0')}:${String(currentSeconds).padStart(2, '0')}.${String(currentMilliseconds).padStart(2, '0')} / ` + | 
					
					
						
						| 
							 | 
						                    `${String(durationMinutes).padStart(2, '0')}:${String(durationSeconds).padStart(2, '0')}.${String(durationMilliseconds).padStart(2, '0')}`; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const progressPercent = (currentTime / duration) * 100; | 
					
					
						
						| 
							 | 
						                if (progressBar) { | 
					
					
						
						| 
							 | 
						                    progressBar.style.width = `${progressPercent}%`; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } catch (error) { | 
					
					
						
						| 
							 | 
						                console.error('時間表示更新エラー:', error); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function togglePlayPause() { | 
					
					
						
						| 
							 | 
						            if (isPlaying) { | 
					
					
						
						| 
							 | 
						                pauseMedia(); | 
					
					
						
						| 
							 | 
						            } else { | 
					
					
						
						| 
							 | 
						                playMedia(); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function playMedia() { | 
					
					
						
						| 
							 | 
						            try { | 
					
					
						
						| 
							 | 
						                if (!combinedAudioElement) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const duration = combinedAudioElement.duration || audioDuration; | 
					
					
						
						| 
							 | 
						                const startTime = parseFloat(startTimeInput.value) || 0; | 
					
					
						
						| 
							 | 
						                const endTime = parseFloat(endTimeInput.value) || duration; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                if (combinedAudioElement.currentTime >= endTime) { | 
					
					
						
						| 
							 | 
						                    combinedAudioElement.currentTime = startTime; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const playPromise = combinedAudioElement.play(); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                if (playPromise !== undefined) { | 
					
					
						
						| 
							 | 
						                    playPromise.then(() => { | 
					
					
						
						| 
							 | 
						                        isPlaying = true; | 
					
					
						
						| 
							 | 
						                        if (playPauseBtn) playPauseBtn.textContent = '⏸'; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                        startSyncCheck(); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                        combinedAudioElement.playbackRate = currentPlaybackRate; | 
					
					
						
						| 
							 | 
						                         | 
					
					
						
						| 
							 | 
						                         | 
					
					
						
						| 
							 | 
						                        checkEndTime(); | 
					
					
						
						| 
							 | 
						                    }).catch(error => { | 
					
					
						
						| 
							 | 
						                        console.error('音声再生エラー:', error); | 
					
					
						
						| 
							 | 
						                        isPlaying = false; | 
					
					
						
						| 
							 | 
						                        if (playPauseBtn) playPauseBtn.textContent = '▶'; | 
					
					
						
						| 
							 | 
						                    }); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } catch (error) { | 
					
					
						
						| 
							 | 
						                console.error('メディア再生エラー:', error); | 
					
					
						
						| 
							 | 
						                isPlaying = false; | 
					
					
						
						| 
							 | 
						                if (playPauseBtn) playPauseBtn.textContent = '▶'; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function pauseMedia() { | 
					
					
						
						| 
							 | 
						            try { | 
					
					
						
						| 
							 | 
						                if (!combinedAudioElement) return; | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                combinedAudioElement.pause(); | 
					
					
						
						| 
							 | 
						                isPlaying = false; | 
					
					
						
						| 
							 | 
						                if (playPauseBtn) playPauseBtn.textContent = '▶'; | 
					
					
						
						| 
							 | 
						                stopSyncCheck(); | 
					
					
						
						| 
							 | 
						            } catch (error) { | 
					
					
						
						| 
							 | 
						                console.error('メディア一時停止エラー:', error); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (progressContainer) { | 
					
					
						
						| 
							 | 
						            progressContainer.addEventListener('click', function(e) { | 
					
					
						
						| 
							 | 
						                if (!combinedAudioElement || !combinedAudioElement.duration) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const rect = this.getBoundingClientRect(); | 
					
					
						
						| 
							 | 
						                const pos = (e.clientX - rect.left) / rect.width; | 
					
					
						
						| 
							 | 
						                const seekTime = pos * combinedAudioElement.duration; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                seekMedia(seekTime); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function seekMedia(time) { | 
					
					
						
						| 
							 | 
						            try { | 
					
					
						
						| 
							 | 
						                if (!combinedAudioElement) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const duration = combinedAudioElement.duration || audioDuration; | 
					
					
						
						| 
							 | 
						                const startTime = parseFloat(startTimeInput.value) || 0; | 
					
					
						
						| 
							 | 
						                const endTime = parseFloat(endTimeInput.value) || duration; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const seekTime = Math.max(startTime, Math.min(time, endTime)); | 
					
					
						
						| 
							 | 
						                combinedAudioElement.currentTime = seekTime; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                if (isPlaying) { | 
					
					
						
						| 
							 | 
						                    combinedAudioElement.play().catch(e => console.error('音声再生エラー:', e)); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } catch (error) { | 
					
					
						
						| 
							 | 
						                console.error('メディアシークエラー:', error); | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (progressContainer) { | 
					
					
						
						| 
							 | 
						            progressContainer.addEventListener('mousemove', function(e) { | 
					
					
						
						| 
							 | 
						                if (!combinedAudioElement || !combinedAudioElement.duration || !progressTime) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const rect = this.getBoundingClientRect(); | 
					
					
						
						| 
							 | 
						                const pos = (e.clientX - rect.left) / rect.width; | 
					
					
						
						| 
							 | 
						                const hoverTime = pos * combinedAudioElement.duration; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const minutes = Math.floor(hoverTime / 60); | 
					
					
						
						| 
							 | 
						                const seconds = Math.floor(hoverTime % 60); | 
					
					
						
						| 
							 | 
						                const milliseconds = Math.floor((hoverTime % 1) * 100); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                progressTime.textContent = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(2, '0')}`; | 
					
					
						
						| 
							 | 
						                progressTime.style.display = 'block'; | 
					
					
						
						| 
							 | 
						                progressTime.style.left = `${pos * 100}%`; | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            progressContainer.addEventListener('mouseleave', function() { | 
					
					
						
						| 
							 | 
						                if (progressTime) { | 
					
					
						
						| 
							 | 
						                    progressTime.style.display = 'none'; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        if (volumeSlider) { | 
					
					
						
						| 
							 | 
						            volumeSlider.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                if (!isAudioCombined) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                lastVolume = parseFloat(this.value); | 
					
					
						
						| 
							 | 
						                applyVolume(); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        if (volumeBtn) { | 
					
					
						
						| 
							 | 
						            volumeBtn.addEventListener('click', function() { | 
					
					
						
						| 
							 | 
						                if (!isAudioCombined) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                if (combinedAudioElement.volume > 0) { | 
					
					
						
						| 
							 | 
						                    lastVolume = parseFloat(volumeSlider.value); | 
					
					
						
						| 
							 | 
						                    volumeSlider.value = 0; | 
					
					
						
						| 
							 | 
						                } else { | 
					
					
						
						| 
							 | 
						                    volumeSlider.value = lastVolume; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                applyVolume(); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function updateVolumeIcon() { | 
					
					
						
						| 
							 | 
						            if (!combinedAudioElement || !isAudioCombined || !volumeBtn) return; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if (combinedAudioElement.volume === 0) { | 
					
					
						
						| 
							 | 
						                volumeBtn.textContent = '🔇'; | 
					
					
						
						| 
							 | 
						            } else if (combinedAudioElement.volume < 0.3) { | 
					
					
						
						| 
							 | 
						                volumeBtn.textContent = '🔈'; | 
					
					
						
						| 
							 | 
						            } else { | 
					
					
						
						| 
							 | 
						                volumeBtn.textContent = '🔊'; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (speedSlider) { | 
					
					
						
						| 
							 | 
						            speedSlider.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                if (!isAudioCombined) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const speed = parseFloat(this.value); | 
					
					
						
						| 
							 | 
						                if (speedValue) speedValue.textContent = speed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						                if (playbackSpeedSlider) playbackSpeedSlider.value = speed; | 
					
					
						
						| 
							 | 
						                if (playbackSpeedValue) playbackSpeedValue.textContent = speed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						                updatePlaybackRate(speed); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (playbackSpeedSlider) { | 
					
					
						
						| 
							 | 
						            playbackSpeedSlider.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                if (!isAudioCombined) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const speed = parseFloat(this.value); | 
					
					
						
						| 
							 | 
						                if (playbackSpeedValue) playbackSpeedValue.textContent = speed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						                if (speedSlider) speedSlider.value = speed; | 
					
					
						
						| 
							 | 
						                if (speedValue) speedValue.textContent = speed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						                updatePlaybackRate(speed); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (tempoInput) { | 
					
					
						
						| 
							 | 
						            tempoInput.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                const tempo = parseFloat(this.value); | 
					
					
						
						| 
							 | 
						                const baseTempo = isTMode ? 66 : 92; | 
					
					
						
						| 
							 | 
						                const speed = tempo / baseTempo; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const clampedSpeed = Math.max(0.001, Math.min(5.0, speed)); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                if (playbackSpeedSlider) playbackSpeedSlider.value = clampedSpeed; | 
					
					
						
						| 
							 | 
						                if (playbackSpeedValue) playbackSpeedValue.textContent = clampedSpeed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						                if (speedSlider) speedSlider.value = clampedSpeed; | 
					
					
						
						| 
							 | 
						                if (speedValue) speedValue.textContent = clampedSpeed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						                if (tempoSpeedValue) tempoSpeedValue.textContent = clampedSpeed.toFixed(2) + 'x'; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                updatePlaybackRate(clampedSpeed); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        function updatePlaybackRate(speed) { | 
					
					
						
						| 
							 | 
						            if (!isAudioCombined || !combinedAudioElement) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            currentPlaybackRate = speed; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            combinedAudioElement.playbackRate = speed; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            combinedAudioElement.preservesPitch = true; | 
					
					
						
						| 
							 | 
						            combinedAudioElement.mozPreservesPitch = true; | 
					
					
						
						| 
							 | 
						            combinedAudioElement.webkitPreservesPitch = true; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        audioSliders.forEach((slider, index) => { | 
					
					
						
						| 
							 | 
						            slider.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                const value = parseFloat(this.value); | 
					
					
						
						| 
							 | 
						                if (volumeValues[index]) { | 
					
					
						
						| 
							 | 
						                    volumeValues[index].textContent = value.toFixed(2); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const percent = value * 100; | 
					
					
						
						| 
							 | 
						                this.style.backgroundSize = `${percent}% 100%`; | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (ffmpegVolumeSlider) { | 
					
					
						
						| 
							 | 
						            ffmpegVolumeSlider.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                const value = parseFloat(this.value); | 
					
					
						
						| 
							 | 
						                if (ffmpegVolumeValue) { | 
					
					
						
						| 
							 | 
						                    ffmpegVolumeValue.textContent = value.toFixed(2); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const percent = (value / 20) * 100; | 
					
					
						
						| 
							 | 
						                this.style.backgroundSize = `${percent}% 100%`; | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        if (globalVolumeSlider) { | 
					
					
						
						| 
							 | 
						            globalVolumeSlider.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                const value = parseFloat(this.value); | 
					
					
						
						| 
							 | 
						                if (globalVolumeValue) globalVolumeValue.textContent = value.toFixed(2); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const percent = value * 100; | 
					
					
						
						| 
							 | 
						                this.style.backgroundSize = `${percent}% 100%`; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                applyVolume(); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (loopCheckbox) { | 
					
					
						
						| 
							 | 
						            loopCheckbox.addEventListener('change', function() { | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (setStartTimeBtn) { | 
					
					
						
						| 
							 | 
						            setStartTimeBtn.addEventListener('click', function() { | 
					
					
						
						| 
							 | 
						                if (combinedAudioElement) { | 
					
					
						
						| 
							 | 
						                    startTimeInput.value = combinedAudioElement.currentTime.toFixed(2); | 
					
					
						
						| 
							 | 
						                    updateProgressMarkers(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (setEndTimeBtn) { | 
					
					
						
						| 
							 | 
						            setEndTimeBtn.addEventListener('click', function() { | 
					
					
						
						| 
							 | 
						                if (combinedAudioElement) { | 
					
					
						
						| 
							 | 
						                    endTimeInput.value = combinedAudioElement.currentTime.toFixed(2); | 
					
					
						
						| 
							 | 
						                    updateProgressMarkers(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (resetEndTimeBtn) { | 
					
					
						
						| 
							 | 
						            resetEndTimeBtn.addEventListener('click', function() { | 
					
					
						
						| 
							 | 
						                if (combinedAudioElement) { | 
					
					
						
						| 
							 | 
						                    endTimeInput.value = combinedAudioElement.duration.toFixed(2); | 
					
					
						
						| 
							 | 
						                    updateProgressMarkers(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function updateProgressMarkers() { | 
					
					
						
						| 
							 | 
						            if (!combinedAudioElement || !isAudioCombined) return; | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            const duration = combinedAudioElement.duration || 0; | 
					
					
						
						| 
							 | 
						            const startTime = parseFloat(startTimeInput.value) || 0; | 
					
					
						
						| 
							 | 
						            const endTime = parseFloat(endTimeInput.value) || duration; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            if (duration > 0) { | 
					
					
						
						| 
							 | 
						                if (startTime > 0 && startMarker) { | 
					
					
						
						| 
							 | 
						                    startMarker.style.left = `${(startTime / duration) * 100}%`; | 
					
					
						
						| 
							 | 
						                    startMarker.style.display = 'block'; | 
					
					
						
						| 
							 | 
						                } else if (startMarker) { | 
					
					
						
						| 
							 | 
						                    startMarker.style.display = 'none'; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                if (endTime < duration && endMarker) { | 
					
					
						
						| 
							 | 
						                    endMarker.style.left = `${(endTime / duration) * 100}%`; | 
					
					
						
						| 
							 | 
						                    endMarker.style.display = 'block'; | 
					
					
						
						| 
							 | 
						                } else if (endMarker) { | 
					
					
						
						| 
							 | 
						                    endMarker.style.display = 'none'; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (startTimeInput) { | 
					
					
						
						| 
							 | 
						            startTimeInput.addEventListener('input', updateProgressMarkers); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						        if (endTimeInput) { | 
					
					
						
						| 
							 | 
						            endTimeInput.addEventListener('input', updateProgressMarkers); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (combineButton) { | 
					
					
						
						| 
							 | 
						            combineButton.addEventListener('click', combineAudio); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (previewButton) { | 
					
					
						
						| 
							 | 
						            previewButton.addEventListener('click', togglePreview); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (syncStatusClose) { | 
					
					
						
						| 
							 | 
						            syncStatusClose.addEventListener('click', function() { | 
					
					
						
						| 
							 | 
						                if (syncStatus) { | 
					
					
						
						| 
							 | 
						                    syncStatus.style.display = 'none'; | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        loadAudioFiles(); | 
					
					
						
						| 
							 | 
						        safeUpdateVolumeIcon(); | 
					
					
						
						| 
							 | 
						        if (volumeSlider) volumeSlider.value = 1; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function initSliderBackgrounds() { | 
					
					
						
						| 
							 | 
						            const sliders = [ | 
					
					
						
						| 
							 | 
						                volumeSlider, | 
					
					
						
						| 
							 | 
						                speedSlider, | 
					
					
						
						| 
							 | 
						                globalVolumeSlider, | 
					
					
						
						| 
							 | 
						                playbackSpeedSlider, | 
					
					
						
						| 
							 | 
						                ffmpegVolumeSlider, | 
					
					
						
						| 
							 | 
						                ...audioSliders | 
					
					
						
						| 
							 | 
						            ].filter(slider => slider !== null); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						            sliders.forEach(slider => { | 
					
					
						
						| 
							 | 
						                if (slider) { | 
					
					
						
						| 
							 | 
						                    slider.style.backgroundImage = 'linear-gradient(#64ffda, #64ffda)'; | 
					
					
						
						| 
							 | 
						                    slider.style.backgroundRepeat = 'no-repeat'; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                    if (slider === globalVolumeSlider) { | 
					
					
						
						| 
							 | 
						                        const percent = slider.value * 100; | 
					
					
						
						| 
							 | 
						                        slider.style.backgroundSize = `${percent}% 100%`; | 
					
					
						
						| 
							 | 
						                        if (globalVolumeValue) globalVolumeValue.textContent = slider.value; | 
					
					
						
						| 
							 | 
						                    } else if (slider === ffmpegVolumeSlider) { | 
					
					
						
						| 
							 | 
						                        const percent = (slider.value / 2) * 100; | 
					
					
						
						| 
							 | 
						                        slider.style.backgroundSize = `${percent}% 100%`; | 
					
					
						
						| 
							 | 
						                        if (ffmpegVolumeValue) ffmpegVolumeValue.textContent = slider.value; | 
					
					
						
						| 
							 | 
						                    } else { | 
					
					
						
						| 
							 | 
						                        slider.style.backgroundSize = `${slider.value * 100}% 100%`; | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        function safeUpdateVolumeIcon() { | 
					
					
						
						| 
							 | 
						            if (combinedAudioElement && isAudioCombined) { | 
					
					
						
						| 
							 | 
						                updateVolumeIcon(); | 
					
					
						
						| 
							 | 
						            } else if (volumeBtn) { | 
					
					
						
						| 
							 | 
						                volumeBtn.textContent = '🔊'; | 
					
					
						
						| 
							 | 
						            } | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        initSliderBackgrounds(); | 
					
					
						
						| 
							 | 
						        safeUpdateVolumeIcon(); | 
					
					
						
						| 
							 | 
						        startSyncCheck();  | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (tempoInput) { | 
					
					
						
						| 
							 | 
						            tempoInput.value = isTMode ? 66 : 92; | 
					
					
						
						| 
							 | 
						            tempoInput.dispatchEvent(new Event('input')); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (playPauseBtn) { | 
					
					
						
						| 
							 | 
						            playPauseBtn.addEventListener('click', togglePlayPause); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    document.addEventListener('DOMContentLoaded', function() { | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const startMarker = document.getElementById('start-marker'); | 
					
					
						
						| 
							 | 
						        const endMarker = document.getElementById('end-marker'); | 
					
					
						
						| 
							 | 
						        const startTimeInput = document.getElementById('start-time'); | 
					
					
						
						| 
							 | 
						        const endTimeInput = document.getElementById('end-time'); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const urlParams = new URLSearchParams(window.location.search); | 
					
					
						
						| 
							 | 
						        const isTMode = urlParams.has('mode') && urlParams.get('mode') === 't'; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const timeMarkers = isTMode ? [{ | 
					
					
						
						| 
							 | 
						            label: '①[1番]', | 
					
					
						
						| 
							 | 
						            time: 15.2 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: '②[1番]', | 
					
					
						
						| 
							 | 
						            time: 43.8 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: '①[2番]', | 
					
					
						
						| 
							 | 
						            time: 79.0 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: '②[2番]', | 
					
					
						
						| 
							 | 
						            time: 108.0 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: '③', | 
					
					
						
						| 
							 | 
						            time: 137.25 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: '④', | 
					
					
						
						| 
							 | 
						            time: 162.55 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: '⑤', | 
					
					
						
						| 
							 | 
						            time: 191.65 | 
					
					
						
						| 
							 | 
						        }] : [{ | 
					
					
						
						| 
							 | 
						            label: 'A', | 
					
					
						
						| 
							 | 
						            time: 8.35 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: 'B', | 
					
					
						
						| 
							 | 
						            time: 72.1 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: 'C', | 
					
					
						
						| 
							 | 
						            time: 98.67 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: 'D', | 
					
					
						
						| 
							 | 
						            time: 155.83 | 
					
					
						
						| 
							 | 
						        }, { | 
					
					
						
						| 
							 | 
						            label: 'E', | 
					
					
						
						| 
							 | 
						            time: 192.55 | 
					
					
						
						| 
							 | 
						        }]; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        const timeMarkersContainer = document.getElementById('time-markers-container'); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        if (timeMarkersContainer) { | 
					
					
						
						| 
							 | 
						            timeMarkers.forEach(marker => { | 
					
					
						
						| 
							 | 
						                const markerElement = document.createElement('div'); | 
					
					
						
						| 
							 | 
						                markerElement.className = 'time-marker'; | 
					
					
						
						| 
							 | 
						                markerElement.innerHTML = `<b>${marker.label}</b>(${marker.time}秒)`; | 
					
					
						
						| 
							 | 
						                markerElement.dataset.time = marker.time; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                markerElement.addEventListener('dragstart', function(e) { | 
					
					
						
						| 
							 | 
						                    e.dataTransfer.setData('text/plain', marker.time); | 
					
					
						
						| 
							 | 
						                    this.classList.add('dragging'); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                markerElement.addEventListener('dragend', function() { | 
					
					
						
						| 
							 | 
						                    this.classList.remove('dragging'); | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                markerElement.draggable = true; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                markerElement.addEventListener('click', function() { | 
					
					
						
						| 
							 | 
						                    if (startTimeInput) { | 
					
					
						
						| 
							 | 
						                        startTimeInput.value = marker.time; | 
					
					
						
						| 
							 | 
						                         | 
					
					
						
						| 
							 | 
						                        if (typeof updateProgressMarkers === 'function') { | 
					
					
						
						| 
							 | 
						                            updateProgressMarkers(); | 
					
					
						
						| 
							 | 
						                        } | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                timeMarkersContainer.appendChild(markerElement); | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        [startTimeInput, endTimeInput].forEach(input => { | 
					
					
						
						| 
							 | 
						            if (!input) return; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            input.addEventListener('dragover', function(e) { | 
					
					
						
						| 
							 | 
						                e.preventDefault(); | 
					
					
						
						| 
							 | 
						                this.style.backgroundColor = 'rgba(100, 255, 218, 0.2)'; | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            input.addEventListener('dragleave', function() { | 
					
					
						
						| 
							 | 
						                this.style.backgroundColor = ''; | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            input.addEventListener('drop', function(e) { | 
					
					
						
						| 
							 | 
						                e.preventDefault(); | 
					
					
						
						| 
							 | 
						                this.style.backgroundColor = ''; | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						                const time = parseFloat(e.dataTransfer.getData('text/plain')); | 
					
					
						
						| 
							 | 
						                if (!isNaN(time)) { | 
					
					
						
						| 
							 | 
						                    this.value = time; | 
					
					
						
						| 
							 | 
						                     | 
					
					
						
						| 
							 | 
						                    if (typeof updateProgressMarkers === 'function') { | 
					
					
						
						| 
							 | 
						                        updateProgressMarkers(); | 
					
					
						
						| 
							 | 
						                    } | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        if (startTimeInput) { | 
					
					
						
						| 
							 | 
						            startTimeInput.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                if (typeof updateProgressMarkers === 'function') { | 
					
					
						
						| 
							 | 
						                    updateProgressMarkers(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						        if (endTimeInput) { | 
					
					
						
						| 
							 | 
						            endTimeInput.addEventListener('input', function() { | 
					
					
						
						| 
							 | 
						                if (typeof updateProgressMarkers === 'function') { | 
					
					
						
						| 
							 | 
						                    updateProgressMarkers(); | 
					
					
						
						| 
							 | 
						                } | 
					
					
						
						| 
							 | 
						            }); | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    document.addEventListener('DOMContentLoaded', () => { | 
					
					
						
						| 
							 | 
						        const urlParams = new URLSearchParams(window.location.search); | 
					
					
						
						| 
							 | 
						        const titleElement = document.getElementById('title-name'); | 
					
					
						
						| 
							 | 
						        if (titleElement) { | 
					
					
						
						| 
							 | 
						            titleElement.textContent = urlParams.get('mode') === 't' ? "地球星歌" : "時の旅人"; | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						    }); | 
					
					
						
						| 
							 | 
						</script> | 
					
					
						
						| 
							 | 
						</body> | 
					
					
						
						| 
							 | 
						</html> |