diff --git a/index.html b/index.html index c459629..ca1e0a0 100644 --- a/index.html +++ b/index.html @@ -190,11 +190,31 @@ canvas{display:block;} + +
Click = add note | Click note = select | Drag = move | Delete/Backspace = remove selected | Scroll = pan timeline + +
+ + @@ -3352,16 +3372,16 @@ function renderScores(){ refreshMiniLB(); // ── MELODY EDITOR ───────────────────────────────────── -var edPiece=null,edMelody=[],edBpm=120,edSelected=-1,edScrollX=0,edDragging=false,edDragNote=-1,edDragOffX=0,edDragOffY=0,edPreviewing=false; +var edPiece=null,edMelody=[],edBpm=120,edBars=8,edSelected=-1,edScrollX=0,edDragging=false,edDragNote=-1,edDragOffX=0,edDragOffY=0,edPreviewing=false,edIsClassical=false,edGenre=""; var ED_LANE_H=50,ED_KICK_H=40,ED_HEAD=0,ED_NOTE_W=14,ED_NOTE_H=16; var ED_COLORS=["#e53e3e","#d69e2e","#3182ce","#38a169","#805ad5"]; var ED_NAMES=["Red","Yellow","Blue","Green","Kick"]; function getEdCanvas(){return document.getElementById("ed-canvas");} function getEdCtx(){return getEdCanvas().getContext("2d");} -function edPxPerSec(){return parseInt(document.getElementById("ed-zoom").value);} -function edTimeToX(t){return t*edPxPerSec()-edScrollX;} -function edXToTime(x){return(x+edScrollX)/edPxPerSec();} +function edPxPerUnit(){return parseInt(document.getElementById("ed-zoom").value);} +function edTimeToX(t){return t*edPxPerUnit()-edScrollX;} +function edXToTime(x){return(x+edScrollX)/edPxPerUnit();} function edYToLane(y){ if(yW)continue; - ctx.strokeStyle=b%4===0?"#444":"#222"; - ctx.lineWidth=b%4===0?1.5:0.5; - ctx.beginPath();ctx.moveTo(bx,ED_HEAD);ctx.lineTo(bx,H);ctx.stroke(); - if(b%4===0){ - ctx.fillStyle="#555";ctx.font="10px sans-serif"; - ctx.fillText((b*beatSec).toFixed(1)+"s",bx+2,ED_HEAD+H-4); + // Grid lines + if(edIsClassical){ + // Classical: grid by beats (seconds / (60/bpm)) + var beatSec=60/edBpm; + var startBeat=Math.floor(edXToTime(0)/beatSec); + var endBeat=Math.ceil(edXToTime(W)/beatSec); + for(var b=startBeat;b<=endBeat;b++){ + var bx=edTimeToX(b*beatSec); + if(bx<0||bx>W)continue; + ctx.strokeStyle=b%4===0?"#444":"#222"; + ctx.lineWidth=b%4===0?1.5:0.5; + ctx.beginPath();ctx.moveTo(bx,ED_HEAD);ctx.lineTo(bx,H);ctx.stroke(); + if(b%4===0){ + ctx.fillStyle="#555";ctx.font="10px sans-serif"; + ctx.fillText((b*beatSec).toFixed(1)+"s",bx+2,H-4); + } + } + }else{ + // Regular rhythm: grid by beat subdivisions (t=0 to 4) + // Draw 16th note grid (every 0.25 beats) + for(var sub=0;sub<=16;sub++){ + var bt=sub*0.25; + var bx=edTimeToX(bt); + if(bx<0||bx>W)continue; + var isBeat=sub%4===0; + var isHalf=sub%2===0; + ctx.strokeStyle=isBeat?"#555":isHalf?"#333":"#1a1a2a"; + ctx.lineWidth=isBeat?2:0.5; + ctx.beginPath();ctx.moveTo(bx,ED_HEAD);ctx.lineTo(bx,H);ctx.stroke(); + if(isBeat){ + ctx.fillStyle="#888";ctx.font="bold 11px sans-serif"; + ctx.fillText("Beat "+(sub/4+1),bx+3,H-4); + } } - } - // Second markers - var startSec=Math.floor(edXToTime(0)); - var endSec=Math.ceil(edXToTime(W)); - for(var s=startSec;s<=endSec;s++){ - var sx=edTimeToX(s); - if(sx<0||sx>W)continue; - ctx.strokeStyle="#333";ctx.lineWidth=0.5; - ctx.beginPath();ctx.moveTo(sx,0);ctx.lineTo(sx,ED_HEAD);ctx.stroke(); } // Lane separator lines @@ -3477,15 +3518,16 @@ function edDraw(){ ctx.globalAlpha=1; }); - // Duration marker + // Duration / pattern end marker if(edPiece){ - var dx=edTimeToX(edPiece.duration); + var endTime=edIsClassical?(edPiece.duration||90):4; // 4 beats for pattern + var dx=edTimeToX(endTime); if(dx>0&&dx=0&&l<=4&&t>=0){ - // Snap to nearest beat subdivision (1/4 beat) - var snap=60/edBpm/4; + // Snap to nearest subdivision + var snap=edIsClassical?(60/edBpm/4):0.25; // 1/4 beat in seconds or 1/16 note in beat units t=Math.round(t/snap)*snap; + t=parseFloat(t.toFixed(4)); + // For regular rhythms, clamp to 0-4 range + if(!edIsClassical)t=Math.max(0,Math.min(3.99,t)); edMelody.push({t:t,l:l}); edMelody.sort(function(a,b){return a.t-b.t;}); edSelected=edMelody.findIndex(function(m){return m.t===t&&m.l===l;}); @@ -3568,14 +3613,15 @@ function edKeyDown(e){ e.preventDefault(); } if(e.key==="ArrowLeft"&&edSelected>=0){ - var snap=60/edBpm/4; - edMelody[edSelected].t=Math.max(0,edMelody[edSelected].t-snap); + var snap=edIsClassical?(60/edBpm/4):0.25; + edMelody[edSelected].t=Math.max(0,parseFloat((edMelody[edSelected].t-snap).toFixed(4))); edMelody.sort(function(a,b){return a.t-b.t;}); edDraw();e.preventDefault(); } if(e.key==="ArrowRight"&&edSelected>=0){ - var snap2=60/edBpm/4; - edMelody[edSelected].t+=snap2; + var snap2=edIsClassical?(60/edBpm/4):0.25; + var maxT=edIsClassical?9999:3.99; + edMelody[edSelected].t=Math.min(maxT,parseFloat((edMelody[edSelected].t+snap2).toFixed(4))); edMelody.sort(function(a,b){return a.t-b.t;}); edDraw();e.preventDefault(); } @@ -3604,23 +3650,39 @@ function edUpdateBpm(){edBpm=parseInt(document.getElementById("ed-bpm").value)|| function edPreview(){ if(edPreviewing){stopPreview();edPreviewing=false;document.getElementById("ed-pv-btn").textContent="▶ Preview";return;} - // Play the piece with current edits var ac=getAc(true);ac.resume(); var piece=edPiece; - for(var i=0;ia.time-b.time);