T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/soundstudiopro.com/public_html/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/public_html/studio_mixer.php
<?php
session_start();

// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
    header('Location: auth/login.php');
    exit;
}

// Set page variables for header
$page_title = 'Studio Mixer - SoundStudioPro';
$page_description = 'Professional multi-track mixing console';
$current_page = 'studio';

// Include header
include 'includes/header.php';

// Get user data
require_once 'config/database.php';
$pdo = getDBConnection();

// Start with no tracks - user will load them manually
$tracks = [];
?>

<div class="mixer-page">
    <div class="mixer-header">
        <h1>🎚️ Studio Mixer</h1>
        <p>Professional Multi-Track Mixing Console</p>
        <button class="btn-load-track" id="loadTrackBtn">
            <i class="fas fa-plus"></i>
            Charger une piste
        </button>
    </div>
    
    <!-- Track Loader Panel -->
    <div class="track-loader-panel" id="trackLoaderPanel">
        <div class="loader-header">
            <h3>📚 Vos pistes disponibles</h3>
            <button class="close-loader" id="closeLoader">
                <i class="fas fa-times"></i>
            </button>
        </div>
        <div class="loader-search">
            <input type="text" id="trackSearch" placeholder="Rechercher une piste..." class="search-input">
        </div>
        <div class="track-list-container" id="trackListContainer">
            <div class="loading-tracks">
                <i class="fas fa-spinner fa-spin"></i>
                <p>Chargement de vos pistes...</p>
            </div>
        </div>
    </div>

    <div class="mixer-container">
        <!-- Transport Controls -->
        <div class="transport-bar">
            <div class="transport-left">
                <button class="transport-btn play-btn" id="mixerPlay">
                    <i class="fas fa-play"></i>
                </button>
                <button class="transport-btn pause-btn" id="mixerPause">
                    <i class="fas fa-pause"></i>
                </button>
                <button class="transport-btn stop-btn" id="mixerStop">
                    <i class="fas fa-stop"></i>
                </button>
            </div>
            <div class="transport-center">
                <div class="time-display">
                    <span id="currentTime">00:00</span>
                    <span class="time-separator">/</span>
                    <span id="totalTime">00:00</span>
                </div>
                <div class="bpm-control">
                    <label>BPM</label>
                    <div class="bpm-input-group">
                        <button class="bpm-btn" id="bpmDown">-</button>
                        <input type="number" id="bpmValue" value="120" min="60" max="200" class="bpm-input">
                        <button class="bpm-btn" id="bpmUp">+</button>
                    </div>
                </div>
            </div>
            <div class="transport-right">
                <button class="transport-btn" id="mixerRecord" title="Record Mix">
                    <i class="fas fa-circle"></i>
                </button>
                <button class="transport-btn" id="mixerLoop" title="Loop">
                    <i class="fas fa-redo"></i>
                </button>
            </div>
        </div>

        <!-- Master Section -->
        <div class="master-channel">
            <div class="master-label-section">
                <div class="master-label">MASTER</div>
                <button class="master-mute-btn" id="masterMute" title="Mute Master">
                    <i class="fas fa-volume-up"></i>
                </button>
            </div>
            <div class="master-fader-section">
                <div class="fader-label">Volume</div>
                <div class="fader-track">
                    <div class="fader-scale">
                        <span class="scale-mark">100</span>
                        <span class="scale-mark">75</span>
                        <span class="scale-mark">50</span>
                        <span class="scale-mark">25</span>
                        <span class="scale-mark">0</span>
                    </div>
                    <div class="fader-knob master-knob" id="masterFader" style="top: 0%;">
                        <div class="fader-grip"></div>
                    </div>
                </div>
                <div class="fader-value" id="masterValue">100</div>
            </div>
            <div class="master-meter-section">
                <div class="meter-label">Level</div>
                <div class="meter-bar">
                    <div class="meter-level" id="masterLevel"></div>
                    <div class="meter-peak" id="masterPeak"></div>
                </div>
            </div>
        </div>

        <!-- Mixer Channels -->
        <div class="mixer-channels" id="mixerChannels">
            <div class="empty-mixer" id="emptyMixer">
                <i class="fas fa-music"></i>
                <p>Chargement de pistes...</p>
                <p style="font-size: 1rem; color: #94a3b8; margin-top: 0.5rem;">Cliquez sur "Charger une piste" pour commencer</p>
            </div>
        </div>
    </div>
</div>

<style>
.mixer-page {
    min-height: 100vh;
    background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%);
    padding: 2rem 0 6rem;
    color: #fff;
}

.mixer-header {
    text-align: center;
    margin-bottom: 3rem;
    padding: 0 2rem;
}

.mixer-header h1 {
    font-size: 3rem;
    font-weight: 900;
    margin-bottom: 0.5rem;
    background: linear-gradient(135deg, #667eea, #764ba2);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.mixer-header p {
    font-size: 1.4rem;
    color: #a0aec0;
    margin-bottom: 1.5rem;
}

.btn-load-track {
    display: inline-flex;
    align-items: center;
    gap: 0.8rem;
    padding: 1rem 2rem;
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    border: none;
    border-radius: 12px;
    font-size: 1.1rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.3s ease;
    box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}

.btn-load-track:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.btn-load-track:active {
    transform: translateY(0);
}

/* Track Loader Panel */
.track-loader-panel {
    position: fixed;
    top: 0;
    right: -400px;
    width: 400px;
    height: 100vh;
    background: linear-gradient(135deg, #1a1a2e, #16213e);
    border-left: 2px solid rgba(102, 126, 234, 0.3);
    box-shadow: -10px 0 40px rgba(0, 0, 0, 0.5);
    z-index: 10000;
    transition: right 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.track-loader-panel.active {
    right: 0;
}

.loader-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1.5rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(0, 0, 0, 0.3);
}

.loader-header h3 {
    margin: 0;
    font-size: 1.4rem;
    color: #e2e8f0;
}

.close-loader {
    width: 36px;
    height: 36px;
    border: none;
    background: rgba(245, 101, 101, 0.2);
    color: #f56565;
    border-radius: 8px;
    font-size: 1.2rem;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.close-loader:hover {
    background: rgba(245, 101, 101, 0.3);
    transform: scale(1.1);
}

.loader-search {
    padding: 1rem 1.5rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.search-input {
    width: 100%;
    padding: 0.9rem 1.2rem;
    background: rgba(0, 0, 0, 0.4);
    border: 2px solid rgba(102, 126, 234, 0.3);
    border-radius: 10px;
    color: white;
    font-size: 1rem;
    transition: all 0.3s ease;
}

.search-input:focus {
    outline: none;
    border-color: rgba(102, 126, 234, 0.6);
    background: rgba(0, 0, 0, 0.5);
}

.search-input::placeholder {
    color: #94a3b8;
}

.track-list-container {
    flex: 1;
    overflow-y: auto;
    padding: 1rem;
}

.track-list-container::-webkit-scrollbar {
    width: 8px;
}

.track-list-container::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.2);
    border-radius: 4px;
}

.track-list-container::-webkit-scrollbar-thumb {
    background: rgba(102, 126, 234, 0.5);
    border-radius: 4px;
}

.track-list-container::-webkit-scrollbar-thumb:hover {
    background: rgba(102, 126, 234, 0.7);
}

.track-item-mixer {
    background: rgba(0, 0, 0, 0.3);
    border: 2px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    padding: 1rem 1.2rem;
    margin-bottom: 0.8rem;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.track-item-mixer:hover {
    background: rgba(102, 126, 234, 0.2);
    border-color: rgba(102, 126, 234, 0.5);
    transform: translateX(-4px);
}

.track-item-mixer.loaded {
    opacity: 0.5;
    cursor: not-allowed;
}

.track-item-mixer.loaded:hover {
    transform: none;
}

.track-item-info {
    flex: 1;
    min-width: 0;
}

.track-item-title {
    font-size: 1rem;
    font-weight: 600;
    color: #e2e8f0;
    margin-bottom: 0.3rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.track-item-meta {
    font-size: 0.85rem;
    color: #94a3b8;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.track-item-action {
    padding: 0.6rem 1rem;
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    border: none;
    border-radius: 8px;
    font-weight: 600;
    font-size: 0.9rem;
    cursor: pointer;
    transition: all 0.3s ease;
    white-space: nowrap;
}

.track-item-action:hover {
    transform: scale(1.05);
    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}

.track-item-action:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
}

.loading-tracks,
.empty-tracks {
    text-align: center;
    padding: 3rem 1rem;
    color: #94a3b8;
}

.loading-tracks i,
.empty-tracks i {
    font-size: 2.5rem;
    margin-bottom: 1rem;
    color: #667eea;
}

.empty-tracks p {
    font-size: 1.1rem;
}

.mixer-container {
    max-width: 1600px;
    margin: 0 auto;
    padding: 0 2rem;
}

/* Transport Bar */
.transport-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: rgba(0, 0, 0, 0.4);
    border: 2px solid rgba(102, 126, 234, 0.3);
    border-radius: 16px;
    padding: 1.5rem 2rem;
    margin-bottom: 2rem;
    backdrop-filter: blur(10px);
}

.transport-left,
.transport-right {
    display: flex;
    gap: 1rem;
}

.transport-center {
    flex: 1;
    text-align: center;
}

.time-display {
    font-size: 1.8rem;
    font-weight: 700;
    color: #e2e8f0;
    font-variant-numeric: tabular-nums;
}

.time-separator {
    margin: 0 1rem;
    color: #667eea;
}

/* BPM Control */
.bpm-control {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1rem;
}

.bpm-control label {
    font-size: 0.85rem;
    color: #94a3b8;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1px;
}

.bpm-input-group {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.bpm-btn {
    width: 36px;
    height: 36px;
    border: 2px solid rgba(102, 126, 234, 0.4);
    background: rgba(102, 126, 234, 0.1);
    color: #667eea;
    border-radius: 8px;
    font-size: 1.2rem;
    font-weight: 700;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.bpm-btn:hover {
    background: rgba(102, 126, 234, 0.3);
    border-color: rgba(102, 126, 234, 0.6);
    transform: scale(1.1);
}

.bpm-input {
    width: 70px;
    padding: 0.6rem;
    background: rgba(0, 0, 0, 0.4);
    border: 2px solid rgba(102, 126, 234, 0.3);
    border-radius: 8px;
    color: #e2e8f0;
    font-size: 1.2rem;
    font-weight: 700;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.bpm-input:focus {
    outline: none;
    border-color: rgba(102, 126, 234, 0.6);
    background: rgba(0, 0, 0, 0.5);
}

.transport-btn {
    width: 50px;
    height: 50px;
    border-radius: 12px;
    border: 2px solid rgba(102, 126, 234, 0.4);
    background: rgba(102, 126, 234, 0.1);
    color: #667eea;
    font-size: 1.4rem;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.transport-btn:hover {
    background: rgba(102, 126, 234, 0.3);
    border-color: rgba(102, 126, 234, 0.6);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}

.transport-btn.active {
    background: linear-gradient(135deg, #667eea, #764ba2);
    border-color: rgba(102, 126, 234, 0.8);
    color: #fff;
    box-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
}

.play-btn.active {
    background: linear-gradient(135deg, #48bb78, #38a169);
    border-color: rgba(72, 187, 120, 0.8);
}

/* Mixer Channels */
.mixer-channels {
    display: flex;
    gap: 1.5rem;
    overflow-x: auto;
    padding: 1rem 0;
    margin-bottom: 2rem;
    scrollbar-width: thin;
    scrollbar-color: rgba(102, 126, 234, 0.5) rgba(0, 0, 0, 0.2);
}

.mixer-channels::-webkit-scrollbar {
    height: 8px;
}

.mixer-channels::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.2);
    border-radius: 4px;
}

.mixer-channels::-webkit-scrollbar-thumb {
    background: rgba(102, 126, 234, 0.5);
    border-radius: 4px;
}

.mixer-channels::-webkit-scrollbar-thumb:hover {
    background: rgba(102, 126, 234, 0.7);
}

.mixer-channel {
    min-width: 120px;
    background: rgba(0, 0, 0, 0.5);
    border: 2px solid rgba(102, 126, 234, 0.2);
    border-radius: 16px;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    transition: all 0.3s ease;
}

.mixer-channel:hover {
    border-color: rgba(102, 126, 234, 0.5);
    background: rgba(0, 0, 0, 0.6);
    transform: translateY(-4px);
    box-shadow: 0 8px 24px rgba(102, 126, 234, 0.2);
}

/* Channel Header */
.channel-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.channel-number {
    width: 24px;
    height: 24px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.85rem;
    color: #fff;
}

.channel-name {
    flex: 1;
    font-size: 0.9rem;
    font-weight: 600;
    color: #e2e8f0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Track Controls */
.track-controls {
    display: flex;
    flex-direction: column;
    gap: 0.8rem;
    padding: 0.8rem;
    background: rgba(0, 0, 0, 0.3);
    border-radius: 10px;
    margin-bottom: 0.5rem;
}

.track-play-btn,
.track-stop-btn {
    width: 100%;
    padding: 0.6rem;
    border: 2px solid rgba(102, 126, 234, 0.4);
    background: rgba(102, 126, 234, 0.1);
    color: #667eea;
    border-radius: 8px;
    font-size: 0.9rem;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}

.track-play-btn:hover {
    background: rgba(72, 187, 120, 0.2);
    border-color: rgba(72, 187, 120, 0.6);
    color: #48bb78;
}

.track-stop-btn:hover {
    background: rgba(245, 101, 101, 0.2);
    border-color: rgba(245, 101, 101, 0.6);
    color: #f56565;
}

.track-play-btn.active {
    background: linear-gradient(135deg, #48bb78, #38a169);
    border-color: rgba(72, 187, 120, 0.8);
    color: #fff;
}

.track-stop-btn.active {
    background: linear-gradient(135deg, #f56565, #e53e3e);
    border-color: rgba(245, 101, 101, 0.8);
    color: #fff;
}

.track-bpm-control {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.track-bpm-control label {
    font-size: 0.75rem;
    color: #94a3b8;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.track-bpm-input-group {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}

.track-bpm-btn {
    width: 28px;
    height: 28px;
    border: 1px solid rgba(102, 126, 234, 0.4);
    background: rgba(102, 126, 234, 0.1);
    color: #667eea;
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: 700;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.track-bpm-btn:hover {
    background: rgba(102, 126, 234, 0.3);
    border-color: rgba(102, 126, 234, 0.6);
}

.track-bpm-input {
    flex: 1;
    padding: 0.4rem 0.5rem;
    background: rgba(0, 0, 0, 0.4);
    border: 1px solid rgba(102, 126, 234, 0.3);
    border-radius: 6px;
    color: #e2e8f0;
    font-size: 0.9rem;
    font-weight: 700;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.track-bpm-input:focus {
    outline: none;
    border-color: rgba(102, 126, 234, 0.6);
    background: rgba(0, 0, 0, 0.5);
}
    overflow: hidden;
    text-overflow: ellipsis;
}

.channel-controls {
    display: flex;
    gap: 0.3rem;
}

.channel-btn {
    width: 28px;
    height: 28px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    background: rgba(255, 255, 255, 0.05);
    border-radius: 6px;
    color: #a0aec0;
    font-size: 0.85rem;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.channel-btn:hover {
    background: rgba(102, 126, 234, 0.2);
    border-color: rgba(102, 126, 234, 0.4);
    color: #667eea;
}

.channel-btn.active {
    background: linear-gradient(135deg, #f56565, #e53e3e);
    border-color: rgba(245, 101, 101, 0.6);
    color: #fff;
}

.solo-btn.active {
    background: linear-gradient(135deg, #f59e0b, #d97706);
    border-color: rgba(245, 158, 11, 0.6);
}

.remove-btn {
    background: rgba(245, 101, 101, 0.1);
    border-color: rgba(245, 101, 101, 0.3);
    color: #f56565;
}

.remove-btn:hover {
    background: rgba(245, 101, 101, 0.2);
    border-color: rgba(245, 101, 101, 0.5);
    color: #fff;
}

/* EQ Section */
.channel-eq {
    display: flex;
    justify-content: space-around;
    gap: 0.5rem;
    padding: 0.5rem 0;
}

.eq-band {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
}

.eq-band label {
    font-size: 0.75rem;
    color: #94a3b8;
    font-weight: 700;
    text-transform: uppercase;
}

.eq-knob-wrapper {
    position: relative;
    width: 45px;
    height: 45px;
}

.eq-knob {
    width: 45px;
    height: 45px;
    border-radius: 50%;
    background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
    border: 2px solid rgba(102, 126, 234, 0.4);
    position: relative;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    transform: rotate(0deg);
}

.eq-knob:hover {
    border-color: rgba(102, 126, 234, 0.7);
    background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(118, 75, 162, 0.3));
    transform: scale(1.1) rotate(0deg);
}

.eq-value {
    font-size: 0.7rem;
    color: #e2e8f0;
    font-weight: 700;
    pointer-events: none;
}

/* Volume Fader */
.channel-fader {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    flex: 1;
}

.fader-track {
    width: 35px;
    height: 220px;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(255, 255, 255, 0.1));
    border: 2px solid rgba(255, 255, 255, 0.15);
    border-radius: 18px;
    position: relative;
    box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.5);
}

.fader-scale {
    position: absolute;
    left: -30px;
    top: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: 8px 0;
    font-size: 0.65rem;
    color: #94a3b8;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.scale-mark {
    height: 1px;
    width: 10px;
    background: rgba(255, 255, 255, 0.3);
    margin-left: auto;
}

.fader-knob {
    position: absolute;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    width: 45px;
    height: 18px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    border: 2px solid rgba(255, 255, 255, 0.3);
    border-radius: 9px;
    cursor: grab;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
    z-index: 10;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
}

.fader-knob:active,
.fader-knob.dragging {
    cursor: grabbing !important;
    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.6);
    transition: none;
}

.fader-grip {
    width: 100%;
    height: 100%;
    background: repeating-linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.2) 0px,
        rgba(255, 255, 255, 0.2) 2px,
        transparent 2px,
        transparent 4px
    );
    border-radius: 7px;
}

.fader-value {
    font-size: 0.9rem;
    color: #e2e8f0;
    font-weight: 700;
    min-height: 1.2rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* Pan Control */
.channel-pan {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
}

.pan-track {
    width: 100%;
    height: 32px;
    background: rgba(0, 0, 0, 0.4);
    border: 2px solid rgba(255, 255, 255, 0.15);
    border-radius: 16px;
    position: relative;
    box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
}

.pan-mark {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.7rem;
    color: #94a3b8;
    font-weight: 700;
    pointer-events: none;
}

.pan-center {
    left: 50%;
    transform: translate(-50%, -50%);
}

.pan-left {
    left: 6px;
}

.pan-right {
    right: 6px;
}

.pan-knob {
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 26px;
    height: 26px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    border: 2px solid rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    cursor: pointer;
    transition: all 0.2s ease;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
    z-index: 10;
}

.pan-knob:hover {
    transform: translate(-50%, -50%) scale(1.15);
    box-shadow: 0 3px 10px rgba(102, 126, 234, 0.5);
}

.pan-indicator {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 3px;
    height: 14px;
    background: #fff;
    transform: translate(-50%, -50%);
    border-radius: 2px;
    pointer-events: none;
}

.pan-value {
    font-size: 0.8rem;
    color: #e2e8f0;
    font-weight: 600;
    min-height: 1.2rem;
    text-align: center;
}

/* Level Meter */
.channel-meter {
    height: 220px;
    width: 100%;
}

.meter-bar {
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.4);
    border: 2px solid rgba(255, 255, 255, 0.15);
    border-radius: 8px;
    position: relative;
    overflow: hidden;
    box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
}

.meter-level {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    background: #667eea;
    transition: height 0.05s linear;
    height: 0%;
    border-radius: 0 0 6px 6px;
}

.meter-peak {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    width: 2px;
    background: #fff;
    height: 0%;
    margin: 0 auto;
    opacity: 0;
    transition: opacity 0.3s ease;
    z-index: 5;
}

.meter-peak.active {
    opacity: 1;
}

/* Master Channel */
.master-channel {
    background: rgba(0, 0, 0, 0.5);
    border: 2px solid rgba(102, 126, 234, 0.3);
    border-radius: 12px;
    padding: 1.2rem 1.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2rem;
    margin-bottom: 2rem;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}

.master-label-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.8rem;
    min-width: 90px;
}

.master-label {
    font-size: 0.85rem;
    font-weight: 900;
    color: #667eea;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    text-align: center;
}

.master-mute-btn {
    width: 36px;
    height: 36px;
    border: 2px solid rgba(245, 101, 101, 0.4);
    background: rgba(245, 101, 101, 0.1);
    border-radius: 8px;
    color: #f56565;
    font-size: 1rem;
    cursor: pointer;
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.master-mute-btn:hover {
    background: rgba(245, 101, 101, 0.2);
    border-color: rgba(245, 101, 101, 0.6);
    transform: scale(1.05);
}

.master-mute-btn.active {
    background: linear-gradient(135deg, #f56565, #e53e3e);
    border-color: rgba(245, 101, 101, 0.8);
    color: #fff;
    box-shadow: 0 0 15px rgba(245, 101, 101, 0.4);
}

.master-mute-btn:not(.active) {
    background: rgba(72, 187, 120, 0.1);
    border-color: rgba(72, 187, 120, 0.4);
    color: #48bb78;
}

.master-mute-btn:not(.active):hover {
    background: rgba(72, 187, 120, 0.2);
    border-color: rgba(72, 187, 120, 0.6);
}

.master-fader-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    width: 80px;
    flex-shrink: 0;
}

.master-fader-section .fader-label {
    font-size: 0.75rem;
    color: #94a3b8;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.master-fader-section .fader-track {
    height: 200px;
    width: 100%;
}

.master-fader-section .fader-value {
    font-size: 0.9rem;
    color: #e2e8f0;
    font-weight: 700;
    min-height: 1.2rem;
    text-align: center;
}

.master-knob {
    background: linear-gradient(135deg, #f59e0b, #d97706);
}

.master-meter-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    width: 60px;
    flex-shrink: 0;
}

.master-meter-section .meter-label {
    font-size: 0.75rem;
    color: #94a3b8;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.master-meter-section .meter-bar {
    height: 200px;
    width: 100%;
}

.empty-mixer {
    text-align: center;
    padding: 4rem 2rem;
    color: #a0aec0;
    grid-column: 1 / -1;
}

.empty-mixer i {
    font-size: 4rem;
    margin-bottom: 1rem;
    color: #667eea;
}

.empty-mixer p {
    font-size: 1.4rem;
    margin-bottom: 2rem;
}

/* Responsive Design */
@media (max-width: 768px) {
    .mixer-header h1 {
        font-size: 2rem;
    }
    
    .mixer-container {
        padding: 0 1rem;
    }
    
    .transport-bar {
        flex-direction: column;
        gap: 1rem;
        padding: 1rem;
    }
    
    .transport-left,
    .transport-right {
        width: 100%;
        justify-content: center;
    }
    
    .mixer-channel {
        min-width: 100px;
    }
    
    .fader-track {
        height: 180px;
    }
    
    .channel-meter {
        height: 180px;
    }
    
    .master-channel {
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        gap: 1.5rem;
        padding: 1rem;
    }
    
    .master-label-section {
        width: auto;
        min-width: 80px;
    }
    
    .master-fader-section,
    .master-meter-section {
        width: auto;
        min-width: 60px;
    }
    
    .master-fader-section .fader-track,
    .master-meter-section .meter-bar {
        height: 150px;
    }
}
</style>

<script>
// Studio Mixer Audio Engine
class StudioMixerEngine {
    constructor() {
        this.audioContext = null;
        this.masterGain = null;
        this.tracks = new Map();
        this.isPlaying = false;
        this.isRecording = false;
        this.isLooping = false;
        this.currentTime = 0;
        this.startTime = 0;
        this.totalTime = 0;
        this.bpm = 120;
        this.playbackRate = 1.0;
    }

    async initialize() {
        try {
            this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
            this.masterGain = this.audioContext.createGain();
            this.masterGain.connect(this.audioContext.destination);
            this.masterGain.gain.setValueAtTime(1.0, this.audioContext.currentTime); // Start at 100%
            
            console.log('🎚️ Studio Mixer Engine initialized');
            return true;
        } catch (error) {
            console.error('Failed to initialize mixer engine:', error);
            return false;
        }
    }

    async loadTrack(trackId, audioUrl) {
        // Ensure audio context is initialized
        if (!this.audioContext) {
            console.error('Audio context not initialized, initializing now...');
            await this.initialize();
        }
        
        try {
            console.log(`📥 Loading track ${trackId} from ${audioUrl}`);
            const response = await fetch(audioUrl);
            if (!response.ok) {
                throw new Error(`Failed to fetch audio: ${response.status} ${response.statusText}`);
            }
            const arrayBuffer = await response.arrayBuffer();
            const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
            console.log(`✅ Track ${trackId} decoded: ${audioBuffer.duration}s, ${audioBuffer.sampleRate}Hz`);
            
            const track = {
                id: trackId,
                buffer: audioBuffer,
                gain: this.audioContext.createGain(),
                pan: this.audioContext.createStereoPanner(),
                eq: {
                    high: this.audioContext.createBiquadFilter(),
                    mid: this.audioContext.createBiquadFilter(),
                    low: this.audioContext.createBiquadFilter()
                },
                source: null,
                isMuted: false,
                isSoloed: false,
                volume: 1.0, // Start at 100% (100 on 0-100 scale)
                panValue: 0,
                eqValues: { high: 0, mid: 0, low: 0 },
                bpm: 120 // Default BPM per track
            };
            
            // Set initial volume
            track.gain.gain.setValueAtTime(1.0, this.audioContext.currentTime);
            
            // Setup EQ filters
            track.eq.high.type = 'highshelf';
            track.eq.high.frequency.setValueAtTime(5000, this.audioContext.currentTime);
            track.eq.mid.type = 'peaking';
            track.eq.mid.frequency.setValueAtTime(1000, this.audioContext.currentTime);
            track.eq.mid.Q.setValueAtTime(1, this.audioContext.currentTime);
            track.eq.low.type = 'lowshelf';
            track.eq.low.frequency.setValueAtTime(200, this.audioContext.currentTime);
            
            // Connect: source -> gain -> eq -> pan -> master
            track.gain.connect(track.eq.high);
            track.eq.high.connect(track.eq.mid);
            track.eq.mid.connect(track.eq.low);
            track.eq.low.connect(track.pan);
            track.pan.connect(this.masterGain);
            
            // Verify master connection
            if (!this.masterGain) {
                console.error('Master gain not initialized!');
            } else {
                console.log(`🎵 Track ${trackId} loaded and connected to master`);
                console.log('Audio chain: source -> gain -> eq.high -> eq.mid -> eq.low -> pan -> masterGain -> destination');
            }
            
            this.tracks.set(trackId, track);
            
            // Update total time
            if (audioBuffer.duration > this.totalTime) {
                this.totalTime = audioBuffer.duration;
                updateTimeDisplay(0, this.totalTime);
            }
            
            return track;
        } catch (error) {
            console.error(`Failed to load track ${trackId}:`, error);
            return null;
        }
    }

    async play() {
        if (this.isPlaying) return;
        if (this.tracks.size === 0) {
            console.warn('No tracks loaded');
            return;
        }
        
        // Ensure audio context is initialized and resumed
        if (!this.audioContext) {
            await this.initialize();
        }
        
        if (this.audioContext.state === 'suspended') {
            try {
                await this.audioContext.resume();
                console.log('✅ Audio context resumed for global play');
            } catch (error) {
                console.error('❌ Failed to resume audio context:', error);
            }
        }
        
        this.isPlaying = true;
        this.startTime = this.audioContext.currentTime - this.currentTime;
        
        // Play all tracks that aren't muted
        const playPromises = [];
        this.tracks.forEach((track, trackId) => {
            if (track.buffer && !track.isMuted) {
                playPromises.push(this.playTrack(trackId));
            }
        });
        
        // Wait for all tracks to start
        await Promise.all(playPromises);
        
        this.updateMeters();
    }

    pause() {
        this.isPlaying = false;
        this.currentTime = this.audioContext.currentTime - this.startTime;
        
        this.tracks.forEach((track) => {
            if (track.source) {
                track.source.stop();
                track.source = null;
            }
        });
    }

    stop() {
        this.isPlaying = false;
        this.currentTime = 0;
        this.startTime = 0;
        
        this.tracks.forEach((track) => {
            if (track.source) {
                track.source.stop();
                track.source = null;
            }
        });
        
        updateTimeDisplay(0, this.totalTime);
    }

    async playTrack(trackId) {
        // Ensure audio context is initialized
        if (!this.audioContext) {
            console.error('Audio context not initialized!');
            await this.initialize();
        }
        
        const track = this.tracks.get(trackId);
        if (!track) {
            console.error(`Track ${trackId} not found in tracks map`);
            return;
        }
        
        if (!track.buffer) {
            console.error(`Track ${trackId} has no audio buffer`);
            return;
        }
        
        // Resume audio context if suspended (required by browser autoplay policy)
        if (this.audioContext.state === 'suspended') {
            try {
                await this.audioContext.resume();
                console.log('✅ Audio context resumed');
            } catch (error) {
                console.error('❌ Failed to resume audio context:', error);
            }
        }
        
        // Stop and disconnect existing source
        if (track.source) {
            try {
                track.source.stop();
                track.source.disconnect();
            } catch (error) {
                // Source might already be stopped
            }
            track.source = null;
        }
        
        // Create new source
        track.source = this.audioContext.createBufferSource();
        track.source.buffer = track.buffer;
        const playbackRate = track.bpm ? (track.bpm / 120) : this.playbackRate;
        track.source.playbackRate.setValueAtTime(playbackRate, this.audioContext.currentTime);
        
        // Connect source to gain
        // Audio routing chain: source -> gain -> eq.high -> eq.mid -> eq.low -> pan -> masterGain -> destination
        // This chain is established in loadTrack(), we just connect the source here
        track.source.connect(track.gain);
        
        // Verify connections
        console.log(`▶️ Playing track ${trackId} at ${playbackRate}x speed`);
        console.log(`Master gain value: ${this.masterGain.gain.value}`);
        console.log(`Track gain value: ${track.gain.gain.value}`);
        console.log(`Audio context state: ${this.audioContext.state}`);
        console.log(`Track buffer duration: ${track.buffer.duration}s`);
        console.log(`Track buffer sample rate: ${track.buffer.sampleRate}Hz`);
        
        // Ensure master is not muted
        const masterMuteBtn = document.getElementById('masterMute');
        if (masterMuteBtn && masterMuteBtn.classList.contains('active')) {
            console.warn('⚠️ Master is muted! Unmuting...');
            masterMuteBtn.classList.remove('active');
            const icon = masterMuteBtn.querySelector('i');
            if (icon) icon.className = 'fas fa-volume-up';
            const currentVolume = parseInt(document.getElementById('masterValue')?.textContent || '50');
            this.setMasterVolume(currentVolume);
        }
        
        try {
            track.source.start(0);
            console.log(`✅ Track ${trackId} started successfully`);
        } catch (error) {
            console.error(`❌ Error starting track ${trackId}:`, error);
            track.source = null;
        }
        
        track.source.onended = () => {
            console.log(`⏹️ Track ${trackId} finished playing`);
            track.source = null;
        };
        
        track.source.onerror = (error) => {
            console.error(`❌ Track ${trackId} playback error:`, error);
            track.source = null;
        };
    }

    stopTrack(trackId) {
        const track = this.tracks.get(trackId);
        if (!track) return;
        
        if (track.source) {
            track.source.stop();
            track.source = null;
        }
    }

    setTrackBPM(trackId, bpm) {
        const track = this.tracks.get(trackId);
        if (!track) return;
        
        const clampedBPM = Math.max(60, Math.min(200, bpm));
        track.bpm = clampedBPM;
        
        // Update playback rate if track is currently playing
        if (track.source && track.source.playbackRate) {
            const playbackRate = clampedBPM / 120;
            track.source.playbackRate.setValueAtTime(playbackRate, this.audioContext.currentTime);
        }
        
        // Update UI
        const bpmInput = document.querySelector(`.track-bpm-input[data-track="${trackId}"]`);
        if (bpmInput) {
            bpmInput.value = clampedBPM;
        }
    }

    setTrackVolume(trackId, volumePercent) {
        const track = this.tracks.get(trackId);
        if (!track) return;
        
        // Convert 0-100 to 0-1 linear gain
        const linearGain = Math.max(0, Math.min(1, volumePercent / 100));
        track.volume = linearGain;
        track.gain.gain.setValueAtTime(linearGain, this.audioContext.currentTime);
    }

    setTrackPan(trackId, panValue) {
        const track = this.tracks.get(trackId);
        if (!track) return;
        
        track.panValue = Math.max(-1, Math.min(1, panValue));
        track.pan.pan.setValueAtTime(track.panValue, this.audioContext.currentTime);
    }

    setTrackEQ(trackId, band, dbValue) {
        const track = this.tracks.get(trackId);
        if (!track || !track.eq[band]) return;
        
        track.eqValues[band] = dbValue;
        const gainValue = Math.pow(10, dbValue / 20);
        track.eq[band].gain.setValueAtTime(gainValue, this.audioContext.currentTime);
    }

    muteTrack(trackId) {
        const track = this.tracks.get(trackId);
        if (!track) return;
        
        track.isMuted = !track.isMuted;
        track.gain.gain.setValueAtTime(track.isMuted ? 0 : track.volume, this.audioContext.currentTime);
    }

    soloTrack(trackId) {
        const track = this.tracks.get(trackId);
        if (!track) return;
        
        track.isSoloed = !track.isSoloed;
        
        // Mute all other tracks if this is soloed
        this.tracks.forEach((otherTrack, otherId) => {
            if (otherId !== trackId) {
                const shouldMute = track.isSoloed;
                otherTrack.gain.gain.setValueAtTime(
                    shouldMute ? 0 : (otherTrack.isMuted ? 0 : otherTrack.volume),
                    this.audioContext.currentTime
                );
            }
        });
    }

    setMasterVolume(volumePercent) {
        // Convert 0-100 to 0-1 linear gain
        const linearGain = Math.max(0, Math.min(1, volumePercent / 100));
        this.masterGain.gain.setValueAtTime(linearGain, this.audioContext.currentTime);
    }

    setBPM(bpm) {
        this.bpm = Math.max(60, Math.min(200, bpm));
        // Calculate playback rate based on BPM (120 BPM = 1.0x speed)
        this.playbackRate = this.bpm / 120;
        
        // Update all playing tracks
        this.tracks.forEach((track, trackId) => {
            if (track.source && track.source.playbackRate) {
                track.source.playbackRate.setValueAtTime(this.playbackRate, this.audioContext.currentTime);
            }
        });
        
        // Update UI
        const bpmInput = document.getElementById('bpmValue');
        if (bpmInput) {
            bpmInput.value = this.bpm;
        }
    }

    updateMeters() {
        if (!this.isPlaying) return;
        
        this.currentTime = this.audioContext.currentTime - this.startTime;
        updateTimeDisplay(this.currentTime, this.totalTime);
        
        // Simulate meter levels (replace with actual audio analysis)
        this.tracks.forEach((track, trackId) => {
            const level = Math.random() * 100;
            updateMeter(trackId, level);
        });
        
        // Master meter
        const masterLevel = Math.random() * 100;
        updateMeter('master', masterLevel);
        
        requestAnimationFrame(() => this.updateMeters());
    }
}

// Initialize mixer engine
const mixerEngine = new StudioMixerEngine();
window.mixerEngine = mixerEngine;

// Track loader state
let availableTracks = [];
let loadedTrackIds = new Set();

// Initialize on page load
document.addEventListener('DOMContentLoaded', async () => {
    await mixerEngine.initialize();
    mixerEngine.setBPM(120); // Initialize BPM
    initializeMixerControls();
    initializeTrackLoader();
    // Don't load tracks automatically - user will load them manually
});

function initializeTrackLoader() {
    // Load Track Button
    document.getElementById('loadTrackBtn')?.addEventListener('click', () => {
        openTrackLoader();
    });
    
    // Close Loader
    document.getElementById('closeLoader')?.addEventListener('click', () => {
        closeTrackLoader();
    });
    
    // Close on background click
    document.getElementById('trackLoaderPanel')?.addEventListener('click', (e) => {
        if (e.target.id === 'trackLoaderPanel') {
            closeTrackLoader();
        }
    });
    
    // Search functionality
    document.getElementById('trackSearch')?.addEventListener('input', (e) => {
        filterTracks(e.target.value);
    });
    
    // Load available tracks
    loadAvailableTracks();
}

async function loadAvailableTracks() {
    try {
        const response = await fetch('/api/get_user_tracks.php?type=all');
        const data = await response.json();
        
        if (data.success && data.tracks) {
            availableTracks = data.tracks;
            renderTrackList(availableTracks);
        } else {
            document.getElementById('trackListContainer').innerHTML = `
                <div class="empty-tracks">
                    <i class="fas fa-music"></i>
                    <p>Aucune piste disponible</p>
                </div>
            `;
        }
    } catch (error) {
        console.error('Error loading tracks:', error);
        document.getElementById('trackListContainer').innerHTML = `
            <div class="empty-tracks">
                <i class="fas fa-exclamation-triangle"></i>
                <p>Erreur lors du chargement</p>
            </div>
        `;
    }
}

function renderTrackList(tracks) {
    const container = document.getElementById('trackListContainer');
    if (!container) return;
    
    if (tracks.length === 0) {
        container.innerHTML = `
            <div class="empty-tracks">
                <i class="fas fa-music"></i>
                <p>Aucune piste trouvée</p>
            </div>
        `;
        return;
    }
    
    container.innerHTML = tracks.map(track => {
        const isLoaded = loadedTrackIds.has(track.id);
        return `
            <div class="track-item-mixer ${isLoaded ? 'loaded' : ''}" data-track-id="${track.id}">
                <div class="track-item-info">
                    <div class="track-item-title" title="${escapeHtml(track.title)}">${escapeHtml(track.title)}</div>
                    <div class="track-item-meta">
                        <span>${escapeHtml(track.music_type || 'Track')}</span>
                        ${track.duration ? `<span>· ${formatTime(track.duration)}</span>` : ''}
                    </div>
                </div>
                <button class="track-item-action" 
                        data-track-id="${track.id}"
                        ${isLoaded ? 'disabled' : ''}
                        onclick="loadTrackToMixer(${track.id}, '${escapeHtml(track.audio_url)}', '${escapeHtml(track.title)}')">
                    ${isLoaded ? '<i class="fas fa-check"></i> Chargée' : '<i class="fas fa-plus"></i> Charger'}
                </button>
            </div>
        `;
    }).join('');
}

function filterTracks(query) {
    const filtered = availableTracks.filter(track => {
        const searchTerm = query.toLowerCase();
        return (track.title || '').toLowerCase().includes(searchTerm) ||
               (track.music_type || '').toLowerCase().includes(searchTerm);
    });
    renderTrackList(filtered);
}

function openTrackLoader() {
    document.getElementById('trackLoaderPanel').classList.add('active');
    document.body.style.overflow = 'hidden';
    loadAvailableTracks();
}

function closeTrackLoader() {
    document.getElementById('trackLoaderPanel').classList.remove('active');
    document.body.style.overflow = '';
    document.getElementById('trackSearch').value = '';
}

function loadTrackToMixer(trackId, audioUrl, trackTitle) {
    // Check if already loaded
    if (loadedTrackIds.has(trackId)) {
        return;
    }
    
    // Add track to mixer
    addTrackChannel(trackId, audioUrl, trackTitle);
    
    // Load track in audio engine
    mixerEngine.loadTrack(trackId, audioUrl).then(track => {
        if (track) {
            loadedTrackIds.add(trackId);
            renderTrackList(availableTracks);
            // Close the loader panel after loading
            closeTrackLoader();
        }
    });
}

function addTrackChannel(trackId, audioUrl, trackTitle) {
    const channelsContainer = document.getElementById('mixerChannels');
    if (!channelsContainer) return;
    
    // Hide empty message
    const emptyMixer = document.getElementById('emptyMixer');
    if (emptyMixer) {
        emptyMixer.style.display = 'none';
    }
    
    // Get next channel number
    const existingChannels = channelsContainer.querySelectorAll('.mixer-channel');
    const channelNumber = existingChannels.length + 1;
    
    const channelHTML = `
        <div class="mixer-channel" data-track-id="${trackId}" data-audio-url="${escapeHtml(audioUrl)}">
            <div class="channel-header">
                <div class="channel-number">${channelNumber}</div>
                <div class="channel-name" title="${escapeHtml(trackTitle)}">
                    ${escapeHtml(trackTitle.length > 12 ? trackTitle.substring(0, 12) + '...' : trackTitle)}
                </div>
                <button class="channel-btn remove-btn" data-track="${trackId}" title="Retirer" onclick="removeTrackFromMixer(${trackId})">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            
            <!-- Track Controls -->
            <div class="track-controls">
                <button class="track-play-btn" data-track="${trackId}" title="Play">
                    <i class="fas fa-play"></i>
                </button>
                <button class="track-stop-btn" data-track="${trackId}" title="Stop">
                    <i class="fas fa-stop"></i>
                </button>
                <div class="track-bpm-control">
                    <label>BPM</label>
                    <div class="track-bpm-input-group">
                        <button class="track-bpm-btn" data-track="${trackId}" data-action="down">-</button>
                        <input type="number" class="track-bpm-input" data-track="${trackId}" value="120" min="60" max="200">
                        <button class="track-bpm-btn" data-track="${trackId}" data-action="up">+</button>
                    </div>
                </div>
            </div>
            <div class="channel-eq">
                <div class="eq-band" data-band="high">
                    <label>H</label>
                    <div class="eq-knob-wrapper">
                        <div class="eq-knob" data-track="${trackId}" data-band="high">
                            <div class="eq-value">0</div>
                        </div>
                    </div>
                </div>
                <div class="eq-band" data-band="mid">
                    <label>M</label>
                    <div class="eq-knob-wrapper">
                        <div class="eq-knob" data-track="${trackId}" data-band="mid">
                            <div class="eq-value">0</div>
                        </div>
                    </div>
                </div>
                <div class="eq-band" data-band="low">
                    <label>L</label>
                    <div class="eq-knob-wrapper">
                        <div class="eq-knob" data-track="${trackId}" data-band="low">
                            <div class="eq-value">0</div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="channel-fader">
                <div class="fader-track">
                    <div class="fader-scale">
                        <span class="scale-mark">100</span>
                        <span class="scale-mark">75</span>
                        <span class="scale-mark">50</span>
                        <span class="scale-mark">25</span>
                        <span class="scale-mark">0</span>
                    </div>
                    <div class="fader-knob" data-track="${trackId}" style="top: 0%;">
                        <div class="fader-grip"></div>
                    </div>
                </div>
                <div class="fader-value" data-track="${trackId}">100</div>
            </div>
            <div class="channel-pan">
                <div class="pan-track">
                    <div class="pan-mark pan-left">L</div>
                    <div class="pan-mark pan-center">C</div>
                    <div class="pan-mark pan-right">R</div>
                    <div class="pan-knob" data-track="${trackId}" style="left: 50%;">
                        <div class="pan-indicator"></div>
                    </div>
                </div>
                <div class="pan-value" data-track="${trackId}">Center</div>
            </div>
            <div class="channel-meter">
                <div class="meter-bar">
                    <div class="meter-level" data-track="${trackId}"></div>
                    <div class="meter-peak" data-track="${trackId}"></div>
                </div>
            </div>
        </div>
    `;
    
    channelsContainer.insertAdjacentHTML('beforeend', channelHTML);
    
    // Initialize controls for new channel
    const newChannel = channelsContainer.querySelector(`[data-track-id="${trackId}"]`);
    if (!newChannel) return;
    
    const fader = newChannel.querySelector('.fader-knob');
    const panKnob = newChannel.querySelector('.pan-knob');
    const eqKnobs = newChannel.querySelectorAll('.eq-knob');
    
    if (fader) makeFaderDraggable(fader);
    if (panKnob) makePanDraggable(panKnob);
    eqKnobs.forEach(knob => makeEQDraggable(knob));
    
    // Initialize track-specific controls
    const playBtn = newChannel.querySelector('.track-play-btn');
    const stopBtn = newChannel.querySelector('.track-stop-btn');
    const bpmInput = newChannel.querySelector('.track-bpm-input');
    const bpmBtns = newChannel.querySelectorAll('.track-bpm-btn');
    
    if (playBtn) {
        playBtn.addEventListener('click', async function() {
            const trackId = this.getAttribute('data-track');
            await mixerEngine.playTrack(trackId);
            this.classList.add('active');
            stopBtn?.classList.remove('active');
        });
    }
    
    if (stopBtn) {
        stopBtn.addEventListener('click', function() {
            const trackId = this.getAttribute('data-track');
            mixerEngine.stopTrack(trackId);
            this.classList.add('active');
            playBtn?.classList.remove('active');
        });
    }
    
    if (bpmInput) {
        bpmInput.addEventListener('change', function() {
            const trackId = this.getAttribute('data-track');
            const bpm = parseInt(this.value) || 120;
            const clampedBPM = Math.max(60, Math.min(200, bpm));
            this.value = clampedBPM;
            mixerEngine.setTrackBPM(trackId, clampedBPM);
        });
        
        bpmInput.addEventListener('input', function() {
            const trackId = this.getAttribute('data-track');
            const bpm = parseInt(this.value) || 120;
            const clampedBPM = Math.max(60, Math.min(200, bpm));
            if (clampedBPM !== bpm) {
                this.value = clampedBPM;
            }
            mixerEngine.setTrackBPM(trackId, clampedBPM);
        });
    }
    
    bpmBtns.forEach(btn => {
        btn.addEventListener('click', function() {
            const trackId = this.getAttribute('data-track');
            const action = this.getAttribute('data-action');
            const input = newChannel.querySelector(`.track-bpm-input[data-track="${trackId}"]`);
            if (input) {
                let currentBPM = parseInt(input.value) || 120;
                if (action === 'up') {
                    currentBPM = Math.min(200, currentBPM + 1);
                } else {
                    currentBPM = Math.max(60, currentBPM - 1);
                }
                input.value = currentBPM;
                mixerEngine.setTrackBPM(trackId, currentBPM);
            }
        });
    });
}

function removeTrackFromMixer(trackId) {
    if (confirm('Retirer cette piste du mixer?')) {
        const channel = document.querySelector(`.mixer-channel[data-track-id="${trackId}"]`);
        if (channel) {
            channel.remove();
        }
        
        // Stop track in engine
        mixerEngine.stopTrack(trackId);
        mixerEngine.tracks.delete(trackId);
        loadedTrackIds.delete(trackId);
        
        // Update track list
        renderTrackList(availableTracks);
        
        // Show empty message if no tracks left
        const channelsContainer = document.getElementById('mixerChannels');
        const existingChannels = channelsContainer.querySelectorAll('.mixer-channel');
        if (existingChannels.length === 0) {
            const emptyMixer = document.getElementById('emptyMixer');
            if (emptyMixer) {
                emptyMixer.style.display = 'block';
            }
        }
    }
}

function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

function loadAllTracks() {
    document.querySelectorAll('.mixer-channel').forEach(channel => {
        const trackId = channel.getAttribute('data-track-id');
        const audioUrl = channel.getAttribute('data-audio-url');
        if (trackId && audioUrl) {
            mixerEngine.loadTrack(trackId, audioUrl).then(track => {
                if (track) {
                    loadedTrackIds.add(trackId);
                }
            });
        }
    });
}

function initializeMixerControls() {
    // Transport controls
    document.getElementById('mixerPlay')?.addEventListener('click', async () => {
        if (mixerEngine.tracks.size === 0) {
            alert('Veuillez charger au moins une piste avant de jouer');
            return;
        }
        await mixerEngine.play();
        document.getElementById('mixerPlay').classList.add('active');
        document.getElementById('mixerPause').classList.remove('active');
    });
    
    document.getElementById('mixerPause')?.addEventListener('click', () => {
        mixerEngine.pause();
        document.getElementById('mixerPlay').classList.remove('active');
        document.getElementById('mixerPause').classList.add('active');
    });
    
    document.getElementById('mixerStop')?.addEventListener('click', () => {
        mixerEngine.stop();
        document.getElementById('mixerPlay').classList.remove('active');
        document.getElementById('mixerPause').classList.remove('active');
    });
    
    document.getElementById('mixerLoop')?.addEventListener('click', function() {
        mixerEngine.isLooping = !mixerEngine.isLooping;
        this.classList.toggle('active');
    });
    
    // BPM Controls
    const bpmInput = document.getElementById('bpmValue');
    const bpmUp = document.getElementById('bpmUp');
    const bpmDown = document.getElementById('bpmDown');
    
    if (bpmInput) {
        bpmInput.addEventListener('change', function() {
            const bpm = parseInt(this.value) || 120;
            mixerEngine.setBPM(bpm);
        });
        
        bpmInput.addEventListener('input', function() {
            const bpm = parseInt(this.value) || 120;
            mixerEngine.setBPM(bpm);
        });
    }
    
    if (bpmUp) {
        bpmUp.addEventListener('click', () => {
            const currentBPM = parseInt(bpmInput.value) || 120;
            mixerEngine.setBPM(currentBPM + 1);
        });
    }
    
    if (bpmDown) {
        bpmDown.addEventListener('click', () => {
            const currentBPM = parseInt(bpmInput.value) || 120;
            mixerEngine.setBPM(currentBPM - 1);
        });
    }
    
    // Fader controls
    document.querySelectorAll('.fader-knob').forEach(fader => {
        makeFaderDraggable(fader);
    });
    
    // Pan controls
    document.querySelectorAll('.pan-knob').forEach(knob => {
        makePanDraggable(knob);
    });
    
    // EQ controls
    document.querySelectorAll('.eq-knob').forEach(knob => {
        makeEQDraggable(knob);
    });
    
    // Mute/Solo buttons (for initial tracks)
    document.querySelectorAll('.mute-btn').forEach(btn => {
        btn.addEventListener('click', function() {
            const trackId = this.getAttribute('data-track');
            mixerEngine.muteTrack(trackId);
            this.classList.toggle('active');
        });
    });
    
    document.querySelectorAll('.solo-btn').forEach(btn => {
        btn.addEventListener('click', function() {
            const trackId = this.getAttribute('data-track');
            mixerEngine.soloTrack(trackId);
            this.classList.toggle('active');
        });
    });
    
    // Master controls
    document.getElementById('masterMute')?.addEventListener('click', function() {
        const isMuted = this.classList.toggle('active');
        const icon = this.querySelector('i');
        
        if (isMuted) {
            // Mute master
            mixerEngine.masterGain.gain.setValueAtTime(0, mixerEngine.audioContext.currentTime);
            if (icon) {
                icon.className = 'fas fa-volume-mute';
            }
        } else {
            // Unmute master - restore to current volume
            const currentVolume = parseInt(document.getElementById('masterValue')?.textContent || '100');
            mixerEngine.setMasterVolume(currentVolume);
            if (icon) {
                icon.className = 'fas fa-volume-up';
            }
        }
    });
}

function makeFaderDraggable(fader) {
    let isDragging = false;
    let startY = 0;
    let startTop = 0;
    const faderTrack = fader.closest('.fader-track');
    const isMaster = fader.classList.contains('master-knob');
    
    fader.addEventListener('mousedown', function(e) {
        isDragging = true;
        startY = e.clientY;
        const currentTop = parseFloat(getComputedStyle(fader).top) || 0;
        startTop = isNaN(currentTop) ? 0 : currentTop;
        fader.classList.add('dragging');
        e.preventDefault();
        e.stopPropagation();
    });
    
    const handleMove = (e) => {
        if (!isDragging || !faderTrack) return;
        
        const deltaY = startY - e.clientY; // Up = higher volume (move knob up)
        const trackHeight = faderTrack.offsetHeight;
        const knobHeight = fader.offsetHeight;
        const maxTop = trackHeight - knobHeight;
        
        // Calculate new top position (0 = top/100%, maxTop = bottom/0%)
        let newTop = Math.max(0, Math.min(maxTop, startTop - deltaY));
        fader.style.top = newTop + 'px';
        
        // Convert to 0-100 scale (top=0 means volume=100%, top=max means volume=0%)
        const percentage = 1 - (newTop / maxTop);
        const volumeValue = Math.max(0, Math.min(100, Math.round(percentage * 100)));
        
        const valueElement = isMaster 
            ? document.getElementById('masterValue')
            : fader.closest('.channel-fader').querySelector('.fader-value');
        
        if (valueElement) {
            valueElement.textContent = volumeValue;
        }
        
        const trackId = fader.getAttribute('data-track');
        if (isMaster) {
            mixerEngine.setMasterVolume(volumeValue);
        } else if (trackId) {
            mixerEngine.setTrackVolume(trackId, volumeValue);
        }
    };
    
    const handleUp = () => {
        if (isDragging) {
            isDragging = false;
            fader.classList.remove('dragging');
        }
    };
    
    document.addEventListener('mousemove', handleMove);
    document.addEventListener('mouseup', handleUp);
    document.addEventListener('mouseleave', handleUp);
    
    // Touch support
    fader.addEventListener('touchstart', function(e) {
        isDragging = true;
        startY = e.touches[0].clientY;
        const currentTop = parseFloat(getComputedStyle(fader).top) || 0;
        startTop = isNaN(currentTop) ? 0 : currentTop;
        fader.classList.add('dragging');
        e.preventDefault();
    });
    
    document.addEventListener('touchmove', (e) => {
        if (!isDragging || !faderTrack) return;
        const touch = e.touches[0];
        const deltaY = startY - touch.clientY;
        const trackHeight = faderTrack.offsetHeight;
        const knobHeight = fader.offsetHeight;
        const maxTop = trackHeight - knobHeight;
        let newTop = Math.max(0, Math.min(maxTop, startTop - deltaY));
        fader.style.top = newTop + 'px';
        
        const percentage = 1 - (newTop / maxTop);
        const volumeValue = Math.max(0, Math.min(100, Math.round(percentage * 100)));
        
        const valueElement = isMaster 
            ? document.getElementById('masterValue')
            : fader.closest('.channel-fader').querySelector('.fader-value');
        
        if (valueElement) {
            valueElement.textContent = volumeValue;
        }
        
        const trackId = fader.getAttribute('data-track');
        if (isMaster) {
            mixerEngine.setMasterVolume(volumeValue);
        } else if (trackId) {
            mixerEngine.setTrackVolume(trackId, volumeValue);
        }
        e.preventDefault();
    });
    
    document.addEventListener('touchend', handleUp);
}

function makePanDraggable(knob) {
    let isDragging = false;
    let startX = 0;
    let startLeft = 0;
    const panTrack = knob.closest('.pan-track');
    
    knob.addEventListener('mousedown', function(e) {
        isDragging = true;
        startX = e.clientX;
        startLeft = parseFloat(getComputedStyle(knob).left) || 50;
        e.preventDefault();
    });
    
    const handleMove = (e) => {
        if (!isDragging) return;
        
        const deltaX = e.clientX - startX;
        const trackWidth = panTrack.offsetWidth;
        const knobWidth = knob.offsetWidth;
        const maxLeft = trackWidth - knobWidth;
        
        let newLeft = Math.max(0, Math.min(maxLeft, startLeft + (deltaX / trackWidth * 100)));
        knob.style.left = newLeft + '%';
        
        const percentage = (newLeft / maxLeft) * 2 - 1;
        const panValue = Math.max(-1, Math.min(1, percentage));
        
        const valueElement = knob.closest('.channel-pan').querySelector('.pan-value');
        if (valueElement) {
            if (Math.abs(panValue) < 0.05) {
                valueElement.textContent = 'Center';
            } else if (panValue > 0) {
                valueElement.textContent = 'R' + Math.round(panValue * 100);
            } else {
                valueElement.textContent = 'L' + Math.round(Math.abs(panValue) * 100);
            }
        }
        
        const trackId = knob.getAttribute('data-track');
        if (trackId) {
            mixerEngine.setTrackPan(trackId, panValue);
        }
    };
    
    document.addEventListener('mousemove', handleMove);
    document.addEventListener('mouseup', () => { isDragging = false; });
    
    // Touch support
    knob.addEventListener('touchstart', function(e) {
        isDragging = true;
        startX = e.touches[0].clientX;
        startLeft = parseFloat(getComputedStyle(knob).left) || 50;
        e.preventDefault();
    });
    
    document.addEventListener('touchmove', (e) => {
        if (!isDragging) return;
        handleMove(e.touches[0]);
        e.preventDefault();
    });
    
    document.addEventListener('touchend', () => { isDragging = false; });
}

function makeEQDraggable(knob) {
    let isDragging = false;
    let startY = 0;
    let startRotation = 0;
    const currentRotation = { value: 0 };
    
    knob.addEventListener('mousedown', function(e) {
        isDragging = true;
        startY = e.clientY;
        startRotation = currentRotation.value;
        e.preventDefault();
    });
    
    const handleMove = (e) => {
        if (!isDragging) return;
        
        const deltaY = startY - e.clientY;
        const maxRotation = 270;
        const rotation = Math.max(-maxRotation, Math.min(maxRotation, startRotation + deltaY * 2));
        currentRotation.value = rotation;
        
        knob.style.transform = `rotate(${rotation}deg)`;
        
        const dbValue = (rotation / maxRotation) * 15;
        const valueElement = knob.querySelector('.eq-value');
        if (valueElement) {
            valueElement.textContent = dbValue > 0 ? '+' + dbValue.toFixed(0) : dbValue.toFixed(0);
        }
        
        const trackId = knob.getAttribute('data-track');
        const band = knob.getAttribute('data-band');
        if (trackId && band) {
            mixerEngine.setTrackEQ(trackId, band, dbValue);
        }
    };
    
    document.addEventListener('mousemove', handleMove);
    document.addEventListener('mouseup', () => { isDragging = false; });
    
    // Touch support
    knob.addEventListener('touchstart', function(e) {
        isDragging = true;
        startY = e.touches[0].clientY;
        startRotation = currentRotation.value;
        e.preventDefault();
    });
    
    document.addEventListener('touchmove', (e) => {
        if (!isDragging) return;
        handleMove(e.touches[0]);
        e.preventDefault();
    });
    
    document.addEventListener('touchend', () => { isDragging = false; });
}

function updateTimeDisplay(current, total) {
    const currentEl = document.getElementById('currentTime');
    const totalEl = document.getElementById('totalTime');
    
    if (currentEl) currentEl.textContent = formatTime(current);
    if (totalEl) totalEl.textContent = formatTime(total);
}

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 updateMeter(trackId, level) {
    let meter, peak;
    
    if (trackId === 'master') {
        meter = document.getElementById('masterLevel');
        peak = document.getElementById('masterPeak');
    } else {
        meter = document.querySelector(`.meter-level[data-track="${trackId}"]`);
        peak = document.querySelector(`.meter-peak[data-track="${trackId}"]`);
    }
    
    if (meter) {
        meter.style.height = Math.min(100, Math.max(0, level)) + '%';
    }
    
    if (peak && level > 90) {
        peak.style.height = level + '%';
        peak.classList.add('active');
        setTimeout(() => peak.classList.remove('active'), 100);
    }
}
</script>

<?php include 'includes/footer.php'; ?>


CasperSecurity Mini