Giriş Yap Ücretsiz Başla
Platform Radarı · BETA

Tek yükleme, sekiz platform kararı

Spotify, Apple Music, YouTube, TikTok, SoundCloud, radyo, kulüp ve arşiv — her birinin loudness hedefi, tepe sınırı ve kanca beklentisi farklı. Parçan hangisine hazır, hangisine değil?

Dosya sunucuya yüklenmez 8 hedef · ayrı kural tablosu Çıkış sırası önerisi

Ses dosyası

Dosyayı sürükle veya tıkla MP3 · WAV · FLAC · OGG · M4A — önerilen en fazla 60 MB
Dosya seçilmedi

Genel tablo —

Eğri parçanın enerji dağılımıdır. Kırmızı kesikli çizgi kırpma, sarı çizgi kanca girişi konumunu gösterir.

Platform başına hazırlık kural tabanlı · ölçümden

Strateji model yorumu

Yapılacaklar öncelik sırası

Nasıl okunur

Platform puanları bildirimsel bir kural tablosundan hesaplanır — tamamen matematik, her yüklemede aynı sonucu verir. Her platformun kendi loudness hedefi, tepe sınırı, mono toleransı ve kanca beklentisi vardır; bu yüzden bir parça bir platformda 95, başka birinde 14 alabilir.

Strateji katmanı ise ölçümleri bir modele yorumlatır: hangi platform en uygun, hangisi riskli, çıkış sırası ne olmalı. Bu katman kesin değildir ve güven değeriyle birlikte gösterilir.

Kırpma ve mono uyumsuzluk geri alınamaz — mastering ile düzeltilemez, kaynağı yeniden export etmek gerekir. Bu yüzden kırpma tespit edilen bir parça tüm platformlarda "uygun değil" işaretlenir.

Aynı şarkı her platformda aynı şekilde davranmaz. Spotify editorial pitch yayından önce ister, TikTok/Instagram kütüphanesi çıkış günü beslenir, YouTube Content ID ayrı bir hak yapısı ister. Platform Radarı parçanın özelliklerini alıp bu sekiz platform için ayrı hazırlık kararları üretir.

Ne soruyor, ne söylüyor?

Neden sıra önemli?

İlk 48 saatte toplanan kaydetme (save) ve playlist sinyali, algoritmanın şarkıyı kime göstereceğini belirler. Tüm platformlar aynı anda açılırsa bu sinyal dağılır. Radar, ilk gün nerede yoğunlaşacağını söyleyerek bu dağılmayı engeller.

Sık sorulan sorular

Bu araç bağlantı kurup veri çekiyor mu?
Hayır. Tamamen yerel bir karar motorudur; hiçbir platform hesabına bağlanmaz, hiçbir şey yüklemez.
Spotify editorial pitch ne zaman gönderilmeli?
Yayından en az 7 gün önce, ideal olarak 3–4 hafta önce. Spotify pitch için minimum süre 7 gündür; daha geç gönderilen pitch'ler editör kuyruğuna düşer.
TikTok için özel bir şey yapmalı mıyım?
Kancanın ilk 15 saniyede geçmesi ve parçanın "sahne" üretmeye uygun bir bölümü olması kritik. Radar bunu parçanın özelliklerine göre değerlendirir.
Ücretsiz mi?
Evet, ücretsiz ve giriş gerektirmez.

İlgili araçlar

Zoneus Tools tarayıcıda çalışan ücretsiz müzik araçlarıdır; ses dosyaların sunucuya yüklenmez. Tüm araçlara göz at.

/*! * Zoneus Lab — tarayıcı içi ses ölçüm motoru (paylaşılan) * ------------------------------------------------------ * Ses dosyası ASLA sunucuya gitmez; yalnızca sayısal özet gönderilir. * Bu dosya tek doğruluk kaynağıdır: tools/analiz.php, tools/platform.php, * tools/mastering.php ve ileride eklenecek ölçüm araçları buradan beslenir. * * Dışa açılan yüzey (window.ZSLab): * coz(file) → Promise * olc(buf,file,ext,onProg) → {olcum, egri, dosya} * lufsHesapla(chans,sr,n) → LUFS (ITU-R BS.1770 yakınlaştırması) * tempoBul(data,sr,n) → {bpm, conf} * kancaHesap(egri,sure) → sn | null * egriCiz(canvas,egri) → enerji eğrisi çizimi * ring(puan,renk,boy) → SVG puan halkası (HTML) * barRenk(p) → eşik renkleri * esc / fmt / tl → yardımcılar * * Ölçülen büyüklükler (olcum): * sure_sn, ornekleme, kanal, bitrate_kbps, format, tepe_db, rms_db, lufs, * crest_db, kirpma_adet, kirpma_orani, dc_offset, mono_korelasyon, zcr, * bas_enerji, orta_enerji, tiz_enerji, sessizlik_basi_ms, sessizlik_sonu_ms, * bpm, bpm_guven, ilk15_enerji_orani, son10_enerji_orani, zirve_konumu, * enerji_degiskenligi, kirpma_bolge, tepe_bolge */ window.ZSLab = (function () { 'use strict'; var MAX_SANIYE = 480; // en fazla 8 dakika ölçülür (bellek güvenliği) var EGRI_PENCERE = 120; // enerji eğrisi pencere sayısı var _actx = null; /* ─────────────── yardımcılar ─────────────── */ function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (m) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[m]; }); } function fmt(n, d) { d = (d == null ? 1 : d); if (n == null || isNaN(n)) return '—'; return (Math.round(n * Math.pow(10, d)) / Math.pow(10, d)).toFixed(d); } /** 3725 → "3:42" */ function tl(sn) { sn = Math.max(0, Math.round(sn || 0)); var d = Math.floor(sn / 60), s = sn % 60; return d + ':' + (s < 10 ? '0' : '') + s; } function dpr() { return window.devicePixelRatio || 1; } /* ─────────────── ses çözme ─────────────── */ function ctx() { if (!_actx) _actx = new (window.AudioContext || window.webkitAudioContext)(); return _actx; } function coz(file) { return new Promise(function (res, rej) { var reader = new FileReader(); reader.onerror = function () { rej(new Error('Dosya okunamadı.')); }; reader.onload = function () { var c = ctx(); var p; try { p = c.decodeAudioData(reader.result.slice(0)); } catch (e) { p = null; } if (!p || !p.then) { // eski tarayıcı: callback tabanlı API try { c.decodeAudioData(reader.result.slice(0), function (buf) { res(buf); }, function () { rej(new Error('Dosya çözülemedi — WAV veya MP3 deneyin.')); }); } catch (e2) { rej(new Error('Dosya çözülemedi — WAV veya MP3 deneyin.')); } return; } p.then(res).catch(function () { rej(new Error('Dosya çözülemedi — WAV veya MP3 deneyin.')); }); }; reader.readAsArrayBuffer(file); }); } /* ─────────────── ≈LUFS: ITU-R BS.1770 ─────────────── */ function lufsHesapla(chans, sr, n) { // Sahne 1: yüksek raf (+4 dB ≈ 1.5 kHz üstü) — standart 48 kHz katsayıları var b1 = [1.53512485958697, -2.69169618940638, 1.19839281085285]; var a1 = [1, -1.69065929318241, 0.73248077421585]; // Sahne 2: yüksek geçiren (≈38 Hz) var b2 = [1, -2, 1]; var a2 = [1, -1.99004745483398, 0.99007225036621]; var blockLen = Math.max(1, Math.floor(sr * 0.4)); // 400 ms var hop = Math.max(1, Math.floor(sr * 0.1)); // 100 ms (%75 örtüşme) if (n < blockLen) return -99; var totalBlocks = Math.floor((n - blockLen) / hop) + 1; var maxBlocks = 4000; var blockStep = Math.max(1, Math.ceil(totalBlocks / maxBlocks)); var nb = Math.ceil(totalBlocks / blockStep); var blocks = new Float64Array(Math.max(1, nb)); for (var c = 0; c < chans.length; c++) { var d = chans[c]; var x1 = 0, x2 = 0, y1 = 0, y2 = 0, u1 = 0, u2 = 0, w1 = 0, w2 = 0; // kayan pencere: halka tamponu + koşan toplam (tam BS.1770, O(1)/örnek) var ring = new Float64Array(blockLen), pos = 0, run = 0, seen = 0, blkIdx = 0; for (var i = 0; i < n; i++) { var x = d[i] || 0; var v1 = b1[0] * x + b1[1] * x1 + b1[2] * x2 - a1[1] * y1 - a1[2] * y2; x2 = x1; x1 = x; y2 = y1; y1 = v1; var v2 = b2[0] * v1 + b2[1] * u1 + b2[2] * u2 - a2[1] * w1 - a2[2] * w2; u2 = u1; u1 = v1; w2 = w1; w1 = v2; var sq = v2 * v2; run -= ring[pos]; ring[pos] = sq; run += sq; pos++; if (pos >= blockLen) pos = 0; seen++; if (seen >= blockLen && ((seen - blockLen) % hop) === 0) { var bi = (blkIdx / blockStep) | 0; if (blkIdx % blockStep === 0 && bi < nb) blocks[bi] += run / blockLen; // kanal güçleri toplanır blkIdx++; } } } var loud = []; for (var k = 0; k < blocks.length; k++) { if (blocks[k] <= 0) continue; var lv = -0.691 + 10 * Math.log10(blocks[k]); if (lv > -70) loud.push(lv); // mutlak eşik } if (!loud.length) return -99; var ort = loud.reduce(function (a, b) { return a + b; }, 0) / loud.length; var gated = loud.filter(function (x) { return x > ort - 10; }); // göreli eşik if (!gated.length) gated = loud; var lin = gated.reduce(function (a, b) { return a + Math.pow(10, b / 10); }, 0) / gated.length; return -0.691 + 10 * Math.log10(lin); } /* ─────────────── BPM: onset zarfı + otokorelasyon ─────────────── */ function tempoBul(data, sr, n) { var maxLen = Math.min(n, sr * 60); var start = Math.max(0, Math.floor((n - maxLen) / 2)); var d = data.subarray ? data.subarray(start, start + maxLen) : data; var frame = Math.floor(sr * 0.01), hop = frame, nF = Math.floor(d.length / hop); if (nF < 40) return { bpm: 0, conf: 0 }; var env = new Float32Array(nF); for (var i = 0; i < nF; i++) { var s = 0, o = i * hop; for (var j = 0; j < frame; j++) { var v = d[o + j] || 0; s += v * v; } env[i] = Math.sqrt(s / frame); } var on = new Float32Array(nF); for (var i2 = 1; i2 < nF; i2++) { var dd = env[i2] - env[i2 - 1]; on[i2] = dd > 0 ? dd : 0; } var minLag = Math.floor(60 / 200 * 100), maxLag = Math.floor(60 / 60 * 100); var best = -1, bestLag = minLag, map = []; for (var lag = minLag; lag <= maxLag; lag++) { var c = 0, norm = 0; for (var i3 = 0; i3 < nF - lag; i3++) { c += on[i3] * on[i3 + lag]; norm += on[i3] * on[i3]; } var sc = norm ? c / norm : 0; sc *= 1 - Math.abs((60 / (lag * 0.01)) - 120) / 400; map.push({ lag: lag, score: sc }); if (sc > best) { best = sc; bestLag = lag; } } map.sort(function (a, b) { return b.score - a.score; }); var bpm = Math.round(60 / ((map[0] || { lag: bestLag }).lag * 0.01)); if (bpm > 160 && bpm / 2 >= 60) bpm = Math.round(bpm / 2); if (bpm < 70 && bpm * 2 <= 200) bpm = Math.round(bpm * 2); return { bpm: bpm, conf: Math.max(15, Math.min(99, Math.round(best * 120))) }; } /* ─────────────── ana ölçüm ─────────────── */ /** * @param {AudioBuffer} buf * @param {File} file * @param {string} ext * @param {function(number,string)=} onProg ilerleme (0-100, mesaj) * @returns {{olcum:Object, egri:number[], dosya:Object}} */ function olc(buf, file, ext, onProg) { var P = function (p, m) { if (typeof onProg === 'function') onProg(p, m); }; P(30, 'Ölçümler alınıyor…'); var sr = buf.sampleRate, chs = buf.numberOfChannels, dur = buf.duration; var MAXS = Math.min(buf.length, Math.floor(sr * MAX_SANIYE)); var L = buf.getChannelData(0); var R = chs > 1 ? buf.getChannelData(1) : null; var n = Math.min(MAXS, L.length); // ── tek geçiş: tepe, RMS, DC, kırpma, korelasyon, ZCR, bant enerjisi ── var peak = 0, sumSq = 0, dcSum = 0, clip = 0, zc = 0, prev = 0; var sLL = 0, sRR = 0, sLR = 0; var bLow = 0, bMid = 0, bHigh = 0; var a1 = 1 - Math.exp(-2 * Math.PI * 200 / sr), a2 = 1 - Math.exp(-2 * Math.PI * 4000 / sr); var lp1 = 0, lp2 = 0; var cnt = 0; // kırpma / tepe konumları (saniye cinsinden kaba dağılım) var ilkKirpmaSn = -1, sonKirpmaSn = -1, ilkTepeSn = -1; for (var i = 0; i < n; i++) { var l = L[i] || 0, r = R ? (R[i] || 0) : l; var m = (l + r) * 0.5; var al = Math.abs(l), ar = Math.abs(r); var am = al > ar ? al : ar; if (am > peak) { peak = am; ilkTepeSn = i / sr; } sumSq += m * m; dcSum += m; cnt++; if (am >= 0.999) { clip++; if (ilkKirpmaSn < 0) ilkKirpmaSn = i / sr; sonKirpmaSn = i / sr; } if ((m >= 0) !== (prev >= 0)) zc++; prev = m; sLL += l * l; sRR += r * r; sLR += l * r; lp1 += a1 * (m - lp1); var low = lp1; var rest = m - lp1; lp2 += a2 * (rest - lp2); var mid = lp2; var high = rest - lp2; bLow += low * low; bMid += mid * mid; bHigh += high * high; } var rms = Math.sqrt(sumSq / Math.max(1, cnt)); var dc = dcSum / Math.max(1, cnt); var peakDb = peak > 0 ? 20 * Math.log10(peak) : -99; var rmsDb = rms > 0 ? 20 * Math.log10(rms) : -99; var crest = Math.max(0, peakDb - rmsDb); var zcr = zc / Math.max(1, cnt); var clipRatio = clip / Math.max(1, cnt); var bTot = bLow + bMid + bHigh || 1; var bLowP = bLow / bTot, bMidP = bMid / bTot, bHighP = bHigh / bTot; var denom = Math.sqrt(Math.max(1e-9, sLL * sRR)); var kor = denom > 0 ? sLR / denom : 0; if (kor > 1) kor = 1; if (kor < -1) kor = -1; P(52, 'Enerji eğrisi çıkarılıyor…'); // ── enerji eğrisi ── var W = EGRI_PENCERE, winLen = Math.floor(n / W), egri = [], winRms = []; for (var w = 0; w < W; w++) { var s0 = w * winLen, e0 = Math.min(n, s0 + winLen), sq = 0, c = 0; var st2 = Math.max(1, Math.floor((e0 - s0) / 4000)); for (var j = s0; j < e0; j += st2) { var mm = (L[j] || 0) + (R ? (R[j] || 0) : 0); mm *= 0.5; sq += mm * mm; c++; } winRms.push(Math.sqrt(sq / Math.max(1, c))); } var maxW = Math.max.apply(null, winRms) || 1; for (var w2 = 0; w2 < W; w2++) egri.push(winRms[w2] / maxW); // ── sessizlik başlangıç/bitiş (eşik: tepe-60 dB) ── var thr = maxW * Math.pow(10, -60 / 20) * 4; var sessBas = 0, sessSon = 0; for (var a = 0; a < W; a++) { if (winRms[a] > thr) { sessBas = Math.round((a / W) * dur * 1000); break; } } for (var b = W - 1; b >= 0; b--) { if (winRms[b] > thr) { sessSon = Math.round(((W - 1 - b) / W) * dur * 1000); break; } } // ── ilk 15 sn / son 10 sn enerji oranları ── var ortW = winRms.reduce(function (x, y) { return x + y; }, 0) / W || 1; var i15 = Math.max(1, Math.round(15 / dur * W)), i10 = Math.max(1, Math.round(10 / dur * W)); var s15 = 0; for (var k = 0; k < i15 && k < W; k++) s15 += winRms[k]; var s10 = 0; for (var k2 = W - 1; k2 >= W - i10 && k2 >= 0; k2--) s10 += winRms[k2]; var ilk15 = (s15 / Math.min(i15, W)) / ortW, son10 = (s10 / Math.min(i10, W)) / ortW; // ── ilk 3 saniye enerjisi (kısa video / TikTok için kritik) ── var i3 = Math.max(1, Math.round(3 / dur * W)); var s3 = 0; for (var k3 = 0; k3 < i3 && k3 < W; k3++) s3 += winRms[k3]; var ilk3 = (s3 / Math.min(i3, W)) / ortW; // ── zirve konumu ve değişkenlik ── var maxIx = 0; for (var z = 0; z < W; z++) { if (winRms[z] > winRms[maxIx]) maxIx = z; } var zirve = W > 1 ? maxIx / (W - 1) : 0; var varSum = 0; for (var v = 0; v < W; v++) { var d2 = winRms[v] - ortW; varSum += d2 * d2; } var degiskenlik = ortW > 0 ? Math.sqrt(varSum / W) / ortW : 0; P(74, 'Loudness ölçülüyor (BS.1770)…'); var lufs = lufsHesapla([L].concat(R ? [R] : []), sr, n); P(88, 'Tempo tespit ediliyor…'); var bpmR = tempoBul(L, sr, n); var bitrate = Math.round(file.size * 8 / Math.max(1, dur) / 1000); var format = (ext || '').toUpperCase(); var olcum = { sure_sn: Math.round(dur * 1000) / 1000, ornekleme: sr, kanal: chs, bitrate_kbps: bitrate, format: format, tepe_db: Math.round(peakDb * 100) / 100, rms_db: Math.round(rmsDb * 100) / 100, lufs: Math.round(lufs * 10) / 10, crest_db: Math.round(crest * 10) / 10, kirpma_adet: clip, kirpma_orani: Math.round(clipRatio * 1e6) / 1e6, dc_offset: Math.round(dc * 1e5) / 1e5, mono_korelasyon: Math.round(kor * 1000) / 1000, zcr: Math.round(zcr * 1e4) / 1e4, bas_enerji: Math.round(bLowP * 1000) / 1000, orta_enerji: Math.round(bMidP * 1000) / 1000, tiz_enerji: Math.round(bHighP * 1000) / 1000, sessizlik_basi_ms: sessBas, sessizlik_sonu_ms: sessSon, bpm: bpmR.bpm, bpm_guven: bpmR.conf, ilk3_enerji_orani: Math.round(ilk3 * 1000) / 1000, ilk15_enerji_orani: Math.round(ilk15 * 1000) / 1000, son10_enerji_orani: Math.round(son10 * 1000) / 1000, zirve_konumu: Math.round(zirve * 1000) / 1000, enerji_degiskenligi: Math.round(degiskenlik * 1000) / 1000, kirpma_ilk_sn: ilkKirpmaSn >= 0 ? Math.round(ilkKirpmaSn * 10) / 10 : -1, kirpma_son_sn: sonKirpmaSn >= 0 ? Math.round(sonKirpmaSn * 10) / 10 : -1, tepe_ilk_sn: ilkTepeSn >= 0 ? Math.round(ilkTepeSn * 10) / 10 : -1, olculen_sn: Math.round((n / sr) * 10) / 10 }; P(100, 'Ölçümler tamam.'); return { olcum: olcum, egri: egri, dosya: { ad: file.name, boyut: file.size, uzanti: format } }; } /* ─────────────── kanca / zirve konumu ─────────────── */ /** İlk belirgin enerji yükselişinin saniyesi (yoksa null). */ function kancaHesap(egri, sure) { if (!egri || !egri.length || !sure) return null; var ort = egri.reduce(function (a, b) { return a + b; }, 0) / egri.length; var thr = ort * 1.18; for (var i = 0; i < egri.length; i++) { if (egri[i] >= thr) return Math.round((i / egri.length) * sure); } return null; } /* ─────────────── çizim ─────────────── */ function egriCiz(cv, e, secenek) { if (!cv || !e || !e.length) return; var o = secenek || {}; var renk = o.renk || '#c9a227'; var zemin = o.zemin || 'rgba(235,190,55,'; var d = dpr(); var w = cv.clientWidth, h = cv.clientHeight; if (!w || !h) return; cv.width = w * d; cv.height = h * d; var g = cv.getContext('2d'); g.setTransform(d, 0, 0, d, 0, 0); g.clearRect(0, 0, w, h); var ort = e.reduce(function (a, b) { return a + b; }, 0) / e.length; g.strokeStyle = 'rgba(120,115,100,.28)'; g.lineWidth = 1; g.setLineDash([4, 4]); g.beginPath(); g.moveTo(0, h - h * ort); g.lineTo(w, h - h * ort); g.stroke(); g.setLineDash([]); var grd = g.createLinearGradient(0, 0, 0, h); grd.addColorStop(0, zemin + '.55)'); grd.addColorStop(1, zemin + '.05)'); g.beginPath(); g.moveTo(0, h); for (var i = 0; i < e.length; i++) { var x = (i / (e.length - 1)) * w; var y = h - Math.max(2, e[i] * h * 0.94); g.lineTo(x, y); } g.lineTo(w, h); g.closePath(); g.fillStyle = grd; g.fill(); g.beginPath(); for (var i2 = 0; i2 < e.length; i2++) { var x2 = (i2 / (e.length - 1)) * w, y2 = h - Math.max(2, e[i2] * h * 0.94); if (i2 === 0) g.moveTo(x2, y2); else g.lineTo(x2, y2); } g.strokeStyle = renk; g.lineWidth = 2; g.stroke(); // isaretli saniyeler (dikey çizgiler) if (o.isaretler && o.isaretler.length && o.sure) { o.isaretler.forEach(function (mk) { if (mk.sn == null || mk.sn < 0) return; var px = (mk.sn / o.sure) * w; if (px < 0 || px > w) return; g.strokeStyle = mk.renk || 'rgba(192,57,43,.55)'; g.lineWidth = 1.5; g.setLineDash([3, 3]); g.beginPath(); g.moveTo(px, 0); g.lineTo(px, h); g.stroke(); g.setLineDash([]); }); } } /* ─────────────── görsel parçalar ─────────────── */ function ring(puan, renk, boy) { boy = boy || 118; var r = boy * 0.44, c = 2 * Math.PI * r; var p = Math.min(100, Math.max(0, puan == null ? 0 : puan)); var off = c * (1 - p / 100); var cx = boy / 2; return '
' + '' + '' + '
' + (puan == null ? '—' : puan) + '/ 100
'; } function barRenk(p) { return p >= 78 ? '#1f7a4d' : (p >= 58 ? '#c9a227' : (p >= 35 ? '#e07b39' : '#c0392b')); } /** Kısa çubuk listesi HTML'i (kırılım tabloları için ortak). */ function cubuklar(kirilim) { if (!kirilim || !kirilim.length) return ''; var h = '
'; kirilim.forEach(function (k) { var p = Math.max(0, Math.min(100, k.puan || 0)); h += '
' + esc(k.etiket) + '' + (k.guven != null ? 'güven %' + Math.round(k.guven * 100) + ' · ' : '') + fmt(p, 1) + '/100
' + '
'; }); return h + '
'; } /** Sıralı aksiyon listesi HTML'i. */ function aksiyonlar(liste) { if (!liste || !liste.length) return ''; var h = '
'; liste.forEach(function (a, n) { h += '
' + (n + 1) + '
' + '
' + esc(a.baslik) + '

' + esc(a.detay) + '

' + '' + (a.kaynak === 'olcum' ? 'ölçüm' : 'model') + '
'; }); return h + '
'; } return { MAX_SANIYE: MAX_SANIYE, coz: coz, ctx: ctx, olc: olc, lufsHesapla: lufsHesapla, tempoBul: tempoBul, kancaHesap: kancaHesap, egriCiz: egriCiz, ring: ring, barRenk: barRenk, cubuklar: cubuklar, aksiyonlar: aksiyonlar, esc: esc, fmt: fmt, tl: tl }; })();