PLAN

Crossfade Implementasyon Planı

Gapless'tan Yumuşak Geçiş'e dönüşüm

Değişecek Dosyalar

# Ana Dosya (Tüm değişiklikler burada)

public/themes/muzibu/js/player/core/player-core.js

# Opsiyonel (Ayar eklenirse)

resources/views/themes/muzibu/components/player.blade.php

Mevcut Sistem (Gapless)

1. preloadNextSong() - Satır ~6800

Sonraki şarkıyı önceden yükler

_preloadedNext = {
    songId: nextSong.song_id,
    song: nextSong,
    hls: hlsPreload,
    audioId: audioId,
    ready: false
}

2. onTrackEnded() - Satır ~5100

Şarkı bitince çağrılır, sonrakine geçer

audio.onended = () => {
    this.onTrackEnded();
}

3. playPreloadedNext() - Satır ~6900

Preload edilmiş şarkıyı başlatır

if (this._preloadedNext && this._preloadedNext.ready) {
    // Hemen başlat
    this._preloadedNext.audio.play();
}

Yapılacak Değişiklikler

1

AudioContext + GainNode Ekle

Her audio element için Web Audio API bağlantısı

// player-core.js başına ekle
_audioContext: null,
_gainNodes: new Map(), // audioId -> gainNode

initAudioContext() {
    if (!this._audioContext) {
        this._audioContext = new (window.AudioContext || window.webkitAudioContext)();
    }
    return this._audioContext;
}

connectToGainNode(audio, audioId) {
    const ctx = this.initAudioContext();
    const source = ctx.createMediaElementSource(audio);
    const gainNode = ctx.createGain();
    source.connect(gainNode);
    gainNode.connect(ctx.destination);
    this._gainNodes.set(audioId, gainNode);
    return gainNode;
}
2

Crossfade Ayarları

Kullanıcı tercihi için ayarlar

// Ayarlar
_crossfadeEnabled: true,      // Crossfade açık/kapalı
_crossfadeDuration: 4,        // Saniye cinsinden (1-10)
_crossfadeInProgress: false,  // Çakışma önleme
3

timeupdate Event Değiştir

Şarkı bitmeden X saniye önce crossfade başlat

// Mevcut timeupdate handler'a ekle
audio.addEventListener('timeupdate', () => {
    // ... mevcut kod ...

    // CROSSFADE TETİKLEME
    if (this._crossfadeEnabled && !this._crossfadeInProgress) {
        const remaining = audio.duration - audio.currentTime;
        if (remaining <= this._crossfadeDuration && remaining > 0) {
            this.startCrossfade();
        }
    }
});
4

startCrossfade() Fonksiyonu

Ana crossfade mantığı

startCrossfade() {
    if (!this._preloadedNext || !this._preloadedNext.ready) {
        return; // Preload hazır değil, gapless devam
    }

    this._crossfadeInProgress = true;
    const duration = this._crossfadeDuration;
    const ctx = this._audioContext;
    const now = ctx.currentTime;

    // Mevcut şarkı: Fade Out
    const currentGain = this._gainNodes.get(this.currentAudioId);
    currentGain.gain.setValueAtTime(1, now);
    currentGain.gain.linearRampToValueAtTime(0, now + duration);

    // Sonraki şarkı: Fade In
    const nextGain = this._gainNodes.get(this._preloadedNext.audioId);
    nextGain.gain.setValueAtTime(0, now);
    nextGain.gain.linearRampToValueAtTime(1, now + duration);

    // Sonraki şarkıyı başlat
    this._preloadedNext.audio.play();

    // Crossfade bitince temizlik
    setTimeout(() => {
        this.finishCrossfade();
    }, duration * 1000);
}
5

finishCrossfade() Fonksiyonu

Crossfade bitince temizlik

finishCrossfade() {
    // Eski şarkıyı durdur
    this.currentAudio.pause();

    // State güncelle
    this.currentSong = this._preloadedNext.song;
    this.currentAudio = this._preloadedNext.audio;
    this.currentAudioId = this._preloadedNext.audioId;
    this.hls = this._preloadedNext.hls;

    // Preload state temizle
    this._preloadedNext = null;
    this._crossfadeInProgress = false;

    // Sonraki şarkıyı preload et
    this.preloadNextSong();
}
6

Fallback: Gapless

Crossfade çalışmazsa otomatik gapless

// Başlangıçta kontrol et
canUseCrossfade() {
    // Web Audio API var mı?
    if (!(window.AudioContext || window.webkitAudioContext)) {
        return false;
    }
    // GainNode test
    try {
        const ctx = new (window.AudioContext || window.webkitAudioContext)();
        ctx.createGain();
        ctx.close();
        return true;
    } catch (e) {
        return false;
    }
}

// init() içinde
if (!this.canUseCrossfade()) {
    this._crossfadeEnabled = false;
    console.log('Crossfade desteklenmiyor, gapless kullanılacak');
}

Dikkat: Preload Buffer

Crossfade için preload buffer'ı biraz artırmak gerekebilir:

# Mevcut (gapless için yeterli)

maxBufferLength: 20 // 2 segment

# Crossfade için önerilen

maxBufferLength: 40 // 4 segment

# Neden? Crossfade başladığında sonraki şarkı

# en az 4-5 saniye buffer'a sahip olmalı

Değişiklik Özeti

# Değişiklik Konum Zorluk
1 AudioContext + GainNode Dosya başı Kolay
2 Crossfade ayarları Dosya başı Kolay
3 timeupdate tetikleme ~satır 5000 Orta
4 startCrossfade() Yeni fonksiyon Orta
5 finishCrossfade() Yeni fonksiyon Orta
6 Fallback kontrolü init() Kolay
7 Preload buffer artır ~satır 6830 Kolay

Sonuç