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
+
+
+
+
+ JavaScript — edit and click "Apply Code" to update the 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);