Meta Description" name="description" />
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>PocketBand Studio DAW</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style>
body { -webkit-tap-highlight-color: transparent; }
/* Vertical range slider look for Android */
.vertical-slider {
writing-mode: bt-lr;
-webkit-appearance: slider-vertical;
width: 24px;
height: 140px;
}
</style>
</head>
<body class="bg-slate-950 text-slate-100 min-h-screen flex flex-col font-sans select-none overflow-x-hidden pb-4">
<header class="bg-slate-900 border-b border-slate-800 p-3 flex flex-wrap items-center justify-between gap-4 sticky top-0 z-50 shadow-md">
<div class="flex items-center gap-3">
<div class="w-4 h-4 rounded-full bg-cyan-500 animate-pulse"></div>
<div>
<h1 class="text-md font-bold tracking-wider text-cyan-400">POCKETBAND STUDIO</h1>
<p class="text-[10px] text-slate-400 font-mono">Mobile Desktop DAW v2.5</p>
</div>
</div>
<div id="screen-display" class="bg-black/40 px-3 py-1.5 rounded-md border border-slate-800 font-mono text-[11px] text-emerald-400 min-w-[220px] flex-1 max-w-sm">
<span class="text-slate-500 mr-2">>></span>Stüdyo Hazır. Dokunun.
</div>
<div class="flex items-center gap-2">
<div class="flex items-center gap-1.5 bg-slate-800 px-2.5 py-1 rounded border border-slate-700">
<span class="text-[10px] font-mono text-slate-400">BPM</span>
<input id="bpm-input" type="number" value="120" min="60" max="240" class="w-12 bg-transparent text-cyan-400 text-sm font-bold font-mono focus:outline-none">
</div>
<button id="play-btn" class="p-2 rounded bg-cyan-500 text-black hover:bg-cyan-600 transition-all font-bold text-xs flex items-center gap-1">
<span>OYNAT</span>
</button>
</div>
</header>
<main class="flex-1 max-w-7xl w-full mx-auto p-2 md:p-4 grid grid-cols-1 lg:grid-cols-12 gap-4">
<section class="lg:col-span-3 flex flex-col gap-4">
<div class="bg-slate-900 border border-slate-800 rounded-lg p-3 shadow-lg">
<h2 class="text-xs font-bold text-slate-400 mb-2 uppercase tracking-widest flex justify-between items-center">
<span>Sinyal Spektrumu</span>
<span class="text-[9px] text-cyan-500">CANLI</span>
</h2>
<div class="relative rounded overflow-hidden border border-slate-950 bg-slate-950 h-28">
<canvas id="visualizer" class="w-full h-full block"></canvas>
</div>
</div>
<div class="bg-slate-900 border border-slate-800 rounded-lg p-3 shadow-lg">
<h2 class="text-xs font-bold text-slate-400 mb-3 uppercase tracking-widest">Hazır Ritimler</h2>
<div class="grid grid-cols-1 gap-2">
<button onclick="loadPreset('techno')" class="w-full text-left px-3 py-2 rounded text-xs bg-slate-800 hover:bg-slate-750 border-l-4 border-cyan-500 text-slate-300">🥁 Techno Beat</button>
<button onclick="loadPreset('rock')" class="w-full text-left px-3 py-2 rounded text-xs bg-slate-800 hover:bg-slate-750 border-l-4 border-amber-500 text-slate-300">🎸 Rock Groove</button>
<button onclick="clearGrid()" class="w-full text-center mt-1 py-1 bg-rose-950/40 text-rose-400 hover:bg-rose-900/40 rounded text-[11px] border border-rose-900">Temizle</button>
</div>
</div>
</section>
<section class="lg:col-span-9 flex flex-col gap-4">
<div class="bg-slate-900 p-1.5 rounded-lg border border-slate-800 flex gap-1 shadow-md">
<button onclick="switchTab('sequencer')" id="tab-sequencer" class="flex-1 py-2 px-3 rounded text-xs font-bold transition-all text-center bg-cyan-500 text-black shadow">DRUM SEQUENCER</button>
<button onclick="switchTab('synth')" id="tab-synth" class="flex-1 py-2 px-3 rounded text-xs font-bold transition-all text-center text-slate-400 hover:bg-slate-800">SYNTHESIZER</button>
<button onclick="switchTab('mixer')" id="tab-mixer" class="flex-1 py-2 px-3 rounded text-xs font-bold transition-all text-center text-slate-400 hover:bg-slate-800">MİKSER</button>
</div>
<div class="bg-slate-900 border border-slate-800 rounded-xl p-4 min-h-[380px] shadow-2xl relative">
<div id="view-sequencer" class="space-y-4">
<h3 class="text-sm font-bold text-cyan-400 uppercase tracking-widest">16 Adımlı Ritim Matrisi</h3>
<div class="overflow-x-auto pb-2">
<div id="sequencer-matrix" class="min-w-[600px] space-y-2">
</div>
</div>
</div>
<div id="view-synth" class="hidden space-y-4">
<div class="flex flex-wrap items-center justify-between gap-2">
<h3 class="text-sm font-bold text-cyan-400 uppercase tracking-widest">Polifonik Sentezleyici</h3>
<select id="wave-select" class="bg-slate-950 text-xs text-cyan-400 border border-slate-800 p-1.5 rounded">
<option value="sawtooth">Sawtooth (Testere)</option>
<option value="sine">Sine (Sinüs)</option>
<option value="square">Square (Kare)</option>
<option value="triangle">Triangle (Üçgen)</option>
</select>
</div>
<div class="overflow-x-auto pt-2">
<div id="piano-keys" class="min-w-[600px] h-40 bg-slate-950 p-2 rounded-lg border border-slate-800 flex relative">
</div>
</div>
</div>
<div id="view-mixer" class="hidden">
<h3 class="text-sm font-bold text-cyan-400 uppercase tracking-widest mb-4">Stüdyo Mikser Masası</h3>
<div class="grid grid-cols-3 gap-4 bg-slate-950 p-4 rounded-xl border border-slate-850 max-w-md mx-auto">
<div class="flex flex-col items-center bg-slate-900 p-3 rounded-lg">
<span class="text-[11px] font-bold text-slate-400">SYNTH</span>
<input type="range" id="vol-synth" min="0" max="1" step="0.05" value="0.7" class="vertical-slider my-4 accent-cyan-500">
</div>
<div class="flex flex-col items-center bg-slate-900 p-3 rounded-lg">
<span class="text-[11px] font-bold text-slate-400">DRUMS</span>
<input type="range" id="vol-drums" min="0" max="1" step="0.05" value="0.8" class="vertical-slider my-4 accent-amber-500">
</div>
<div class="flex flex-col items-center bg-slate-900 p-3 rounded-lg border border-cyan-900">
<span class="text-[11px] font-bold text-cyan-400">MASTER</span>
<input type="range" id="vol-master" min="0" max="1" step="0.05" value="0.8" class="vertical-slider my-4 accent-cyan-400">
</div>
</div>
</div>
</div>
</section>
</main>
<script>
// --- AUDIO ENGINE CORES ---
let audioCtx = null;
let masterGain, synthGain, drumsGain;
let analyser, isPlaying = false;
let currentStep = 0;
let bpm = 120;
let timerId = null;
const grid = {
kick: Array(16).fill(false),
snare: Array(16).fill(false),
hihat: Array(16).fill(false)
};
const notes = [
{f: 130.81, n: "C3"}, {f: 146.83, n: "D3"}, {f: 164.81, n: "E3"},
{f: 174.61, n: "F3"}, {f: 196.00, n: "G3"}, {f: 220.00, n: "A3"},
{f: 246.94, n: "B3"}, {f: 261.63, n: "C4"}, {f: 293.66, n: "D4"}
];
function initAudio() {
if (audioCtx) return;
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
masterGain = audioCtx.createGain();
masterGain.gain.value = document.getElementById('vol-master').value;
analyser = audioCtx.createAnalyser();
analyser.fftSize = 64;
synthGain = audioCtx.createGain();
synthGain.gain.value = document.getElementById('vol-synth').value;
drumsGain = audioCtx.createGain();
drumsGain.gain.value = document.getElementById('vol-drums').value;
synthGain.connect(masterGain);
drumsGain.connect(masterGain);
masterGain.connect(analyser);
analyser.connect(audioCtx.destination);
startVisualizer();
}
// --- SYNTH GENERATOR ---
function playNote(freq) {
initAudio();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = document.getElementById('wave-select').value;
osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
gain.gain.setValueAtTime(0, audioCtx.currentTime);
gain.gain.linearRampToValueAtTime(synthGain.gain.value, audioCtx.currentTime + 0.05);
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.4);
osc.connect(gain);
gain.connect(synthGain);
osc.start();
osc.stop(audioCtx.currentTime + 0.45);
}
// --- SYNTH DRUM ENGINES ---
function playKick(time) {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.frequency.setValueAtTime(120, time);
osc.frequency.exponentialRampToValueAtTime(0.01, time + 0.12);
gain.gain.setValueAtTime(drumsGain.gain.value, time);
gain.gain.exponentialRampToValueAtTime(0.001, time + 0.12);
osc.connect(gain); gain.connect(drumsGain);
osc.start(time); osc.stop(time + 0.13);
}
function playSnare(time) {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(180, time);
gain.gain.setValueAtTime(drumsGain.gain.value * 0.7, time);
gain.gain.exponentialRampToValueAtTime(0.001, time + 0.1);
osc.connect(gain); gain.connect(drumsGain);
osc.start(time); osc.stop(time + 0.11);
}
function playHihat(time) {
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'square';
osc.frequency.setValueAtTime(8000, time);
gain.gain.setValueAtTime(drumsGain.gain.value * 0.3, time);
gain.gain.exponentialRampToValueAtTime(0.001, time + 0.04);
osc.connect(gain); gain.connect(drumsGain);
osc.start(time); osc.stop(time + 0.05);
}
// --- SEQUENCER TIMING ---
function runSequencer() {
if (!isPlaying) return;
const stepTime = (60 / bpm) / 4;
if (grid.kick[currentStep]) playKick(audioCtx.currentTime);
if (grid.snare[currentStep]) playSnare(audioCtx.currentTime);
if (grid.hihat[currentStep]) playHihat(audioCtx.currentTime);
// UI Highlight update
document.querySelectorAll('.step-btn').forEach(b => b.classList.remove('ring-2', 'ring-white'));
document.querySelectorAll(`.step-${currentStep}`).forEach(b => b.classList.add('ring-2', 'ring-white'));
currentStep = (currentStep + 1) % 16;
timerId = setTimeout(runSequencer, stepTime * 1000);
}
// --- UI BUILDERS ---
function buildSequencer() {
const matrix = document.getElementById('sequencer-matrix');
matrix.innerHTML = '';
['kick', 'snare', 'hihat'].forEach(track => {
const row = document.createElement('div');
row.className = 'flex items-center gap-1';
row.innerHTML = `<span class="w-16 text-xs uppercase font-bold text-slate-400">${track}</span>`;
const gridDiv = document.createElement('div');
gridDiv.className = 'flex-1 grid grid-cols-16 gap-1';
for(let i=0; i<16; i++) {
const btn = document.createElement('button');
btn.className = `h-10 rounded text-[10px] font-mono font-bold step-btn step-${i} ${grid[track][i] ? 'bg-cyan-500 text-black':'bg-slate-950 text-slate-700'}`;
btn.innerText = i+1;
btn.onclick = () => {
initAudio();
grid[track][i] = !grid[track][i];
btn.className = `h-10 rounded text-[10px] font-mono font-bold step-btn step-${i} ${grid[track][i] ? 'bg-cyan-500 text-black':'bg-slate-950 text-slate-700'}`;
};
gridDiv.appendChild(btn);
}
row.appendChild(gridDiv);
matrix.appendChild(row);
});
}
function buildPiano() {
const p = document.getElementById('piano-keys');
p.innerHTML = '';
notes.forEach((n, idx) => {
const b = document.createElement('button');
b.className = "flex-1 bg-slate-100 hover:bg-slate-200 text-slate-900 font-bold text-xs rounded-b pb-2 flex flex-col justify-end items-center border-r border-slate-300 active:bg-cyan-400";
b.innerHTML = `<span>${n.n}</span>`;
b.onmousedown = b.ontouchstart = (e) => { e.preventDefault(); playNote(n.f); };
p.appendChild(b);
});
}
// --- CONTROL LISTENERS ---
document.getElementById('play-btn').onclick = function() {
initAudio();
isPlaying = !isPlaying;
if(isPlaying) {
this.innerText = "DURDUR";
this.className = "p-2 rounded bg-rose-600 text-white transition-all font-bold text-xs";
runSequencer();
} else {
this.innerText = "OYNAT";
this.className = "p-2 rounded bg-cyan-500 text-black transition-all font-bold text-xs";
clearTimeout(timerId);
}
};
document.getElementById('bpm-input').onchange = function() {
bpm = Math.max(60, Math.min(240, Number(this.value)));
};
document.getElementById('vol-synth').oninput = function() { if(synthGain) synthGain.gain.value = this.value; };
document.getElementById('vol-drums').oninput = function() { if(drumsGain) drumsGain.gain.value = this.value; };
document.getElementById('vol-master').oninput = function() { if(masterGain) masterGain.gain.value = this.value; };
function switchTab(tab) {
['sequencer', 'synth', 'mixer'].forEach(t => {
document.getElementById(`view-${t}`).classList.add('hidden');
document.getElementById(`tab-${t}`).className = "flex-1 py-2 px-3 rounded text-xs font-bold text-slate-400 hover:bg-slate-800";
});
document.getElementById(`view-${tab}`).classList.remove('hidden');
document.getElementById(`tab-${tab}`).className = "flex-1 py-2 px-3 rounded text-xs font-bold bg-cyan-500 text-black shadow";
}
function loadPreset(type) {
clearGrid();
if(type === 'techno') {
grid.kick = [true, false, false, false, true, false, false, false, true, false, false, false, true, false, false, false];
grid.hihat = [false, false, true, false, false, false, true, false, false, false, true, false, false, false, true, true];
} else if(type === 'rock') {
grid.kick = [true, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false];
grid.snare = [false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false];
}
buildSequencer();
}
function clearGrid() {
['kick', 'snare', 'hihat'].forEach(t => grid[t].fill(false));
buildSequencer();
}
function startVisualizer() {
const canvas = document.getElementById('visualizer');
const ctx = canvas.getContext('2d');
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
function draw() {
requestAnimationFrame(draw);
analyser.getByteFrequencyData(dataArray);
ctx.fillStyle = '#0f172a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const barWidth = (canvas.width / bufferLength) * 2.5;
let x = 0;
for(let i = 0; i < bufferLength; i++) {
const barHeight = dataArray[i] / 3;
ctx.fillStyle = `rgb(34, 211, 238)`;
ctx.fillRect(x, canvas.height - barHeight, barWidth - 1, barHeight);
x += barWidth;
}
}
draw();
}
// Build UI on start
buildSequencer();
buildPiano();
</script>
</body>
</html>
2
2
86KB
288KB
251.0ms
320.0ms
251.0ms