chordlab v2.2

This commit is contained in:
lrdlg
2026-07-16 17:36:44 +02:00
parent 5f1d3fc5c7
commit d5c4c3be6d
7 changed files with 1678 additions and 75 deletions
+3 -55
View File
@@ -246,7 +246,7 @@
</div>
<div class="lcd-right">
<div>VOICING <b id="lcdVoicing">0</b> · <b id="lcdDrop">CLOSE</b></div>
<div>KEY <b id="lcdKey">OFF</b> · PERF <b id="lcdPerf">BLOCK</b> · B <b id="lcdBassPerf">TENUE</b></div>
<div>KEY <b id="lcdKey">OFF</b> · PERF <b id="lcdPerf">BLOCK</b></div>
<div>MIDI <b id="lcdMidi"></b> · VL <b id="lcdVl">OFF</b></div>
</div>
</div>
@@ -333,20 +333,6 @@
<option value="-12" selected>1 oct</option>
</select>
</div>
<div class="field"><label>rythme B</label>
<select id="bassPerfSel">
<option value="held">tenue</option>
<option value="quarter">noires</option>
<option value="eighth">croches</option>
<option value="offbeat">contretemps</option>
<option value="octave">octaves</option>
<option value="root5">fond.quinte</option>
<option value="walk">boogie</option>
<option value="tresillo">332</option>
<option value="pump">pompe</option>
<option value="rnd">aléatoire</option>
</select>
</div>
<div class="toggle-line">
<button class="hwbtn" id="keyBtn"><span class="led"></span>KEY</button>
<select id="keyRoot"></select>
@@ -449,7 +435,7 @@
</div>
</div>
<footer>_chordlab v2.1 · logique d'accords inspirée de l'Orchid ORC-1 (implémentation indépendante étendue)</footer>
<footer>_chordlab v2.2 · logique d'accords inspirée de l'Orchid ORC-1 (implémentation indépendante étendue)</footer>
</div>
<script>
@@ -479,7 +465,6 @@ const state = {
playing:false, loop:false, playIdx:-1, playTimer:null,
vlAuto:false,
perf:"block", rate:0.25,
bassPerf:"held",
humanize:false,
};
@@ -677,35 +662,6 @@ function suggestNext(){
RENDU D'ÉVÉNEMENTS — partagé audio / MIDI live / export
events: [{n, at (beats), dur (beats), vel, isBass}]
========================================================= */
function renderBassEvents(bassMidi, beats, hV, hT){
const p = state.bassPerf, ev = [];
const push = (n, at, dur, vel=110)=>{
at = Math.max(0, at + hT());
dur = Math.min(dur, Math.max(0.1, beats - at - 0.02));
ev.push({n, at, dur, vel:vel+hV(), isBass:true});
};
if (p==="held"){ push(bassMidi, 0, beats*0.97); }
else if (p==="quarter"){ for(let t=0;t<beats;t+=1) push(bassMidi,t,0.9); }
else if (p==="eighth"){ for(let t=0;t<beats;t+=0.5) push(bassMidi,t,0.42); }
else if (p==="offbeat"){ for(let t=0.5;t<beats;t+=1) push(bassMidi,t,0.42); }
else if (p==="octave"){ let i=0; for(let t=0;t<beats;t+=0.5) push(i++%2?bassMidi+12:bassMidi,t,0.42); }
else if (p==="root5"){ let i=0; for(let t=0;t<beats;t+=1) push(i++%2?bassMidi+7:bassMidi,t,0.9); }
else if (p==="walk"){ const seq=[0,7,12,7]; let i=0; for(let t=0;t<beats;t+=1) push(bassMidi+seq[i++%4],t,0.9); }
else if (p==="tresillo"){
for(let c=0;c<beats;c+=4)
[0,1.5,3].forEach(o=>{ if(c+o<beats) push(bassMidi,c+o,1.2,114); });
}
else if (p==="pump"){ for(let t=0;t<beats;t+=1) push(bassMidi,t,0.38,114); }
else if (p==="rnd"){
const opts=[0,0,7,12];
for(let t=0;t<beats;t+=0.5)
if (Math.random()<0.65) push(bassMidi+opts[Math.floor(Math.random()*opts.length)],t,0.42);
if (!ev.length) push(bassMidi,0,0.9); // jamais de silence total
}
else { push(bassMidi, 0, beats*0.97); }
return ev;
}
function renderEvents(chord, beats, perfOverride){
const perf = perfOverride || state.perf;
const rate = state.rate;
@@ -714,7 +670,7 @@ function renderEvents(chord, beats, perfOverride){
const hV = ()=> hum ? Math.round((Math.random()-0.5)*20) : 0;
const hT = ()=> hum ? (Math.random()-0.5)*0.03 : 0;
if (chord.bass !== null && chord.bass !== undefined)
ev.push(...renderBassEvents(chord.bass, beats, hV, hT));
ev.push({n:chord.bass, at:0, dur:beats*0.97, vel:110+hV(), isBass:true});
const notes = chord.notes;
if (perf==="block"){
notes.forEach(n=>ev.push({n, at:Math.max(0,hT()), dur:beats*0.95, vel:100+hV(), isBass:false}));
@@ -913,7 +869,6 @@ function saveSession(){
drop:state.drop, slash:state.slash, bass:state.bass, bassOct:state.bassOct,
keyMode:state.keyMode, keyRoot:state.keyRoot, keyScale:state.keyScale,
perf:state.perf, rate:state.rate, humanize:state.humanize,
bassPerf:state.bassPerf,
bpm:getBpm(), vlAuto:state.vlAuto,
},
progression:state.progression,
@@ -943,7 +898,6 @@ function loadSession(file){
state.keyScale=s.keyScale||"major";
state.perf=s.perf||"block";
state.rate=s.rate||0.25;
state.bassPerf=s.bassPerf||"held";
state.humanize=!!s.humanize;
state.vlAuto=!!s.vlAuto;
state.progression=Array.isArray(d.progression)?d.progression:[];
@@ -966,8 +920,6 @@ function syncUiFromState(){
$("keyRoot").value=state.keyRoot;
$("keyScaleSel").value=state.keyScale;
$("perfSel").value=state.perf; $("rateSel").value=state.rate;
$("bassPerfSel").value=state.bassPerf;
$("lcdBassPerf").textContent=$("bassPerfSel").selectedOptions[0].textContent.toUpperCase();
$("humanBtn").classList.toggle("on", state.humanize);
$("vlBtn").classList.toggle("on", state.vlAuto);
$("lcdDrop").textContent=state.drop.toUpperCase();
@@ -1134,10 +1086,6 @@ $("perfSel").addEventListener("change",function(){
$("lcdPerf").textContent=this.selectedOptions[0].textContent.toUpperCase();
});
$("rateSel").addEventListener("change",function(){state.rate=parseFloat(this.value);});
$("bassPerfSel").addEventListener("change",function(){
state.bassPerf=this.value;
$("lcdBassPerf").textContent=this.selectedOptions[0].textContent.toUpperCase();
});
$("humanBtn").addEventListener("click",function(){
state.humanize=!state.humanize;
this.classList.toggle("on",state.humanize);