show index-in-pattern
s-ol
27 days ago
11 | 11 |
</main>
|
12 | 12 |
<aside class="pattern controls">
|
13 | 13 |
<div class="control control--preset">
|
14 | |
<label for="pattern-preset">preset</label>
|
|
14 |
<label for="pattern-preset">pattern</label>
|
15 | 15 |
<select id="pattern-preset">
|
16 | 16 |
<option value="custom">(custom)</option>
|
17 | 17 |
<optgroup label="scales">
|
|
29 | 29 |
<option value="minor-3-">diminished</option>
|
30 | 30 |
</optgroup>
|
31 | 31 |
</select>
|
32 | |
</label>
|
33 | 32 |
</div>
|
34 | 33 |
<div class="control">
|
35 | |
<label for="pattern-len">(semi)tones</label>
|
|
34 |
<label for="pattern-len">octave size</label>
|
36 | 35 |
<input id="pattern-len" type="number" min="4" value="12" />
|
37 | 36 |
</div>
|
38 | 37 |
<div class="steps">
|
|
99 | 98 |
</svg>
|
100 | 99 |
<div class="controls">
|
101 | 100 |
<div class="control control--preset">
|
102 | |
<label for="layout-preset">preset</label>
|
|
101 |
<label for="layout-preset">layout</label>
|
103 | 102 |
<select id="layout-preset">
|
104 | 103 |
<option value="wicki-hayden">Wicki/Hayden</option>
|
105 | 104 |
<option value="janko">Jankó</option>
|
|
107 | 106 |
<option value="gerhard">Gerhard</option>
|
108 | 107 |
<option value="custom">(custom)</option>
|
109 | 108 |
</select>
|
110 | |
</label>
|
111 | 109 |
</div>
|
112 | 110 |
<div class="control control--axis">
|
113 | 111 |
<label for="step-1">up</label>
|
47 | 47 |
}
|
48 | 48 |
});
|
49 | 49 |
|
50 | |
const presetName = Object.keys(PRESETS).find(k => completeState(PRESETS[k]).join(',') === full.join(','));
|
|
50 |
const presetName = Object.keys(PRESETS).find(k => completeState(PRESETS[k]).sort().join(',') === full.sort().join(','));
|
51 | 51 |
preset.value = presetName ?? 'custom';
|
52 | 52 |
|
53 | 53 |
updateFocus();
|
102 | 102 |
bg.font = `${size*0.5}px sans-serif`;
|
103 | 103 |
bg.textAlign = 'center';
|
104 | 104 |
bg.textBaseline = 'middle';
|
105 | |
|
106 | |
bg.strokeStyle = '#ff0000';
|
107 | |
bg.strokeWidth = 5;
|
108 | 105 |
|
109 | 106 |
const rot = layout.getRot();
|
110 | 107 |
const [qq, rr] = layout.getSteps();
|
|
123 | 120 |
const note = q*qq + r*rr;
|
124 | 121 |
let step = note % length;
|
125 | 122 |
step = (step + length) % length;
|
|
123 |
const oct = Math.floor(note / length);
|
|
124 |
const stepIndex = steps.indexOf(note);
|
126 | 125 |
|
127 | |
if (steps.includes(note)) {
|
128 | |
bg.fillStyle = '#eeeeee';
|
129 | |
hexagon(bg);
|
|
126 |
hexagon(bg);
|
|
127 |
if (stepIndex > -1) {
|
|
128 |
bg.fillStyle = '#bae3f5';
|
130 | 129 |
bg.fill();
|
|
130 |
|
|
131 |
bg.beginPath();
|
|
132 |
bg.arc(0, 0, size*0.65, 0, Math.PI*2);
|
131 | 133 |
}
|
132 | |
|
|
134 |
|
|
135 |
const c = (1 - Math.abs(oct)/10) * 255;
|
|
136 |
bg.fillStyle = `rgb(${c},${c},${c})`;
|
|
137 |
bg.fill();
|
|
138 |
|
133 | 139 |
bg.rotate(-rot);
|
134 | 140 |
bg.fillStyle = '#303336';
|
135 | |
bg.fillText(step, 0, 0);
|
|
141 |
if (stepIndex > -1) {
|
|
142 |
bg.fillText(step, 0, -size*0.3);
|
|
143 |
|
|
144 |
bg.fillStyle = '#505557';
|
|
145 |
bg.fillText(`(${stepIndex})`, 0, size*0.3);
|
|
146 |
} else {
|
|
147 |
bg.fillText(step, 0, 0);
|
|
148 |
}
|
136 | 149 |
|
137 | 150 |
bg.restore();
|
138 | 151 |
}
|
44 | 44 |
|
45 | 45 |
patternLength.onchange = () => {
|
46 | 46 |
length = +patternLength.value;
|
|
47 |
if (!length || length < 4) length = 4;
|
47 | 48 |
pattern = pattern.filter(n => n < length);
|
48 | 49 |
update();
|
49 | 50 |
};
|