atome 0.5.7.5.9 → 0.5.7.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/atome.gemspec +1 -1
- data/lib/atome/version.rb +1 -1
- data/vendor/assets/application/examples/timeline.rb +132 -0
- data/vendor/assets/src/index.html +1 -1
- data/vendor/assets/src/index_opal.html +1 -1
- data/vendor/assets/src/index_server.html +1 -1
- data/vendor/assets/src/index_server_wasm.html +1 -1
- data/vendor/assets/src/index_wasm.html +1 -1
- data/vendor/assets/src/js/atome/atome.js +9 -1
- data/vendor/assets/src/js/third_parties/a_pianoroll.js +1676 -0
- data/vendor/assets/src/medias/images/waveform.png +0 -0
- metadata +5 -17
- data/vendor/assets/src/js/third_parties/webaudio-pianoroll.min.js +0 -66
@@ -0,0 +1,1676 @@
|
|
1
|
+
customElements.define("webaudio-pianoroll", class Pianoroll extends HTMLElement {
|
2
|
+
constructor() {
|
3
|
+
super();
|
4
|
+
this.noteIdCounter = 0;
|
5
|
+
this.editing = true
|
6
|
+
this.refuse = false
|
7
|
+
this.tool = 'create'
|
8
|
+
}
|
9
|
+
|
10
|
+
defineprop() {
|
11
|
+
const plist = this.module.properties;
|
12
|
+
for (let k in plist) {
|
13
|
+
const v = plist[k];
|
14
|
+
this["_" + k] = this.getAttr(k, v.value);
|
15
|
+
Object.defineProperty(this, k, {
|
16
|
+
get: () => {
|
17
|
+
return this["_" + k]
|
18
|
+
},
|
19
|
+
set: (val) => {
|
20
|
+
this["_" + k] = val;
|
21
|
+
if (typeof (this[v.observer]) == "function")
|
22
|
+
this[v.observer]();
|
23
|
+
}
|
24
|
+
});
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
|
29
|
+
marker(position, id, label) {
|
30
|
+
const playhead = document.createElement("div");
|
31
|
+
playhead.className = "marker";
|
32
|
+
playhead.style.position = "absolute";
|
33
|
+
playhead.style.left = `${(position - this.xoffset) * this.stepw + this.yruler + this.kbwidth}px`;
|
34
|
+
|
35
|
+
// Ajout de l'id et du label comme contenu de la div
|
36
|
+
playhead.id = id;
|
37
|
+
playhead.dataset.id = id;
|
38
|
+
playhead.dataset.label = label;
|
39
|
+
playhead.textContent = label;
|
40
|
+
|
41
|
+
// Création du trait rouge
|
42
|
+
const locator = document.createElement("div");
|
43
|
+
locator.style.position = "absolute";
|
44
|
+
locator.style.width = "2px"; // Épaisseur du trait
|
45
|
+
locator.style.height = "100%"; // S'étend sur toute la hauteur du conteneur
|
46
|
+
locator.style.backgroundColor = "red";
|
47
|
+
locator.style.left = '0px'; // Positionner au centre du playhead
|
48
|
+
locator.style.top = '0px';
|
49
|
+
locator.style.transform = "translateX(-50%)"; // Centrer précisément le trait
|
50
|
+
|
51
|
+
playhead.appendChild(locator);
|
52
|
+
|
53
|
+
playhead.addEventListener('click', () => {
|
54
|
+
console.log(`Playhead ID: ${id}`);
|
55
|
+
});
|
56
|
+
|
57
|
+
playhead.addEventListener('mousedown', (e) => {
|
58
|
+
const initialX = e.clientX;
|
59
|
+
const initialLeft = parseInt(playhead.style.left, 10);
|
60
|
+
|
61
|
+
const onMouseMove = (e) => {
|
62
|
+
const deltaX = e.clientX - initialX;
|
63
|
+
let newLeft = initialLeft + deltaX;
|
64
|
+
|
65
|
+
let newPosition = (newLeft - this.yruler - this.kbwidth) / this.stepw + this.xoffset;
|
66
|
+
|
67
|
+
newPosition = Math.round(newPosition / this.snap) * this.snap;
|
68
|
+
|
69
|
+
newLeft = (newPosition - this.xoffset) * this.stepw + this.yruler + this.kbwidth;
|
70
|
+
playhead.style.left = `${newLeft}px`;
|
71
|
+
|
72
|
+
console.log(`Playhead ${id} moved to quantized position: ${newPosition}`);
|
73
|
+
|
74
|
+
const markerEvent = this.sequence.find(ev => ev.id === id && ev.type === 'marker');
|
75
|
+
if (markerEvent) {
|
76
|
+
markerEvent.t = newPosition;
|
77
|
+
console.log(`Updated event in sequence for marker ID: ${id}, new position: ${newPosition}`);
|
78
|
+
}
|
79
|
+
};
|
80
|
+
|
81
|
+
const onMouseUp = () => {
|
82
|
+
document.removeEventListener('mousemove', onMouseMove);
|
83
|
+
document.removeEventListener('mouseup', onMouseUp);
|
84
|
+
};
|
85
|
+
|
86
|
+
document.addEventListener('mousemove', onMouseMove);
|
87
|
+
document.addEventListener('mouseup', onMouseUp);
|
88
|
+
});
|
89
|
+
|
90
|
+
this.canvas.parentElement.appendChild(playhead);
|
91
|
+
const ev = {
|
92
|
+
id: id,
|
93
|
+
t: position,
|
94
|
+
n: 0, // makers have no note info
|
95
|
+
g: 0, // makers have no length
|
96
|
+
f: 0, // not selected
|
97
|
+
type: 'marker',
|
98
|
+
details: {label: label, element: playhead},
|
99
|
+
textureApplied: false
|
100
|
+
};
|
101
|
+
|
102
|
+
this.sequence.push(ev);
|
103
|
+
}
|
104
|
+
|
105
|
+
removeMarker(id) {
|
106
|
+
const playhead = document.getElementById(id);
|
107
|
+
if (playhead) {
|
108
|
+
playhead.parentElement.removeChild(playhead);
|
109
|
+
console.log(`Playhead with ID: ${id} has been removed.`);
|
110
|
+
} else {
|
111
|
+
console.log(`Playhead with ID: ${id} does not exist.`);
|
112
|
+
}
|
113
|
+
const eventIndex = this.sequence.findIndex(ev => ev.id === id && ev.type === 'marker');
|
114
|
+
if (eventIndex !== -1) {
|
115
|
+
this.sequence.splice(eventIndex, 1);
|
116
|
+
console.log(`Event associated with marker ID: ${id} has been removed from the sequence.`);
|
117
|
+
} else {
|
118
|
+
console.log(`No event found in the sequence for marker ID: ${id}.`);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
connectedCallback() {
|
123
|
+
let root;
|
124
|
+
root = this;
|
125
|
+
this.module = {
|
126
|
+
is: "webaudio-pianoroll",
|
127
|
+
properties: {
|
128
|
+
lowestnote: {type: Number, value: 33, observer: 'layout'},
|
129
|
+
highestnote: {type: Number, value: 39, observer: 'layout'},
|
130
|
+
width: {type: Number, value: 640, observer: 'layout'},
|
131
|
+
height: {type: Number, value: 320, observer: 'layout'},
|
132
|
+
timebase: {type: Number, value: 16, observer: 'layout'},
|
133
|
+
editmode: {type: String, value: "dragpoly"},
|
134
|
+
xrange: {type: Number, value: 16, observer: 'layout'},
|
135
|
+
yrange: {type: Number, value: 16, observer: 'layout'},
|
136
|
+
xoffset: {type: Number, value: 0, observer: 'layout'},
|
137
|
+
yoffset: {type: Number, value: 60, observer: 'layout'},
|
138
|
+
grid: {type: Number, value: 4},
|
139
|
+
snap: {type: Number, value: 1},
|
140
|
+
wheelzoom: {type: Number, value: 0},
|
141
|
+
wheelzoomx: {type: Number, value: 0},
|
142
|
+
wheelzoomy: {type: Number, value: 0},
|
143
|
+
xscroll: {type: Number, value: 0},
|
144
|
+
yscroll: {type: Number, value: 0},
|
145
|
+
gridnoteratio: {type: Number, value: 0.5, observer: 'updateTimer'},
|
146
|
+
xruler: {type: Number, value: 24, observer: 'layout'},
|
147
|
+
yruler: {type: Number, value: 24, observer: 'layout'},
|
148
|
+
octadj: {type: Number, value: -1},
|
149
|
+
cursor: {type: Number, value: 0, observer: 'redrawMarker'},
|
150
|
+
markstart: {type: Number, value: 0, observer: 'redrawMarker'},
|
151
|
+
markend: {type: Number, value: 8, observer: 'redrawMarker'},
|
152
|
+
defvelo: {type: Number, value: 100},
|
153
|
+
collt: {type: String, value: "#ccc"},
|
154
|
+
coldk: {type: String, value: "#aaa"},
|
155
|
+
colgrid: {type: String, value: "#666"},
|
156
|
+
colnote: {type: String, value: "#f22"},
|
157
|
+
colnotesel: {type: String, value: "#0f0"},
|
158
|
+
colnoteborder: {type: String, value: "#000"},
|
159
|
+
colnoteselborder: {type: String, value: "#fff"},
|
160
|
+
colrulerbg: {type: String, value: "#666"},
|
161
|
+
colrulerfg: {type: String, value: "#fff"},
|
162
|
+
colrulerborder: {type: String, value: "#000"},
|
163
|
+
colselarea: {type: String, value: "rgba(0,0,0,0.3)"},
|
164
|
+
bgsrc: {type: String, value: null, observer: 'layout'},
|
165
|
+
cursorsrc: {
|
166
|
+
type: String,
|
167
|
+
value: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj4NCjxwYXRoIGZpbGw9InJnYmEoMjU1LDEwMCwxMDAsMC44KSIgZD0iTTAsMSAyNCwxMiAwLDIzIHoiLz4NCjwvc3ZnPg0K"
|
168
|
+
},
|
169
|
+
cursoroffset: {type: Number, value: 0},
|
170
|
+
markstartsrc: {
|
171
|
+
type: String,
|
172
|
+
value: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4NCjxwYXRoIGZpbGw9IiMwYzAiIGQ9Ik0wLDEgMjQsMSAwLDIzIHoiLz4NCjwvc3ZnPg0K"
|
173
|
+
},
|
174
|
+
markstartoffset: {type: Number, value: 0},
|
175
|
+
markendsrc: {
|
176
|
+
type: String,
|
177
|
+
value: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4NCjxwYXRoIGZpbGw9IiMwYzAiIGQ9Ik0wLDEgMjQsMSAyNCwyMyB6Ii8+DQo8L3N2Zz4NCg=="
|
178
|
+
},
|
179
|
+
markendoffset: {type: Number, value: -24},
|
180
|
+
kbsrc: {
|
181
|
+
type: String,
|
182
|
+
value: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSI0ODAiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiPgo8cGF0aCBmaWxsPSIjZmZmIiBzdHJva2U9IiMwMDAiIGQ9Ik0wLDAgaDI0djQ4MGgtMjR6Ii8+CjxwYXRoIGZpbGw9IiMwMDAiIGQ9Ik0wLDQwIGgxMnY0MGgtMTJ6IE0wLDEyMCBoMTJ2NDBoLTEyeiBNMCwyMDAgaDEydjQwaC0xMnogTTAsMzIwIGgxMnY0MGgtMTJ6IE0wLDQwMCBoMTJ2NDBoLTEyeiIvPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAiIGQ9Ik0wLDYwIGgyNCBNMCwxNDAgaDI0IE0wLDIyMCBoMjQgTTAsMjgwIGgyNCBNMCwzNDAgaDI0IE0wLDQyMCBoMjQiLz4KPC9zdmc+Cg==",
|
183
|
+
observer: 'layout'
|
184
|
+
},
|
185
|
+
kbwidth: {type: Number, value: 40},
|
186
|
+
loop: {type: Number, value: 0},
|
187
|
+
preload: {type: Number, value: 1.0},
|
188
|
+
tempo: {type: Number, value: 120, observer: 'updateTimer'},
|
189
|
+
enable: {type: Boolean, value: true},
|
190
|
+
},
|
191
|
+
};
|
192
|
+
this.defineprop();
|
193
|
+
root.innerHTML =
|
194
|
+
`<style>
|
195
|
+
.pianoroll{
|
196
|
+
background:#ccc;
|
197
|
+
}
|
198
|
+
:host {
|
199
|
+
user-select: none;
|
200
|
+
display: inline-block;
|
201
|
+
font-family: sans-serif;
|
202
|
+
font-size: 11px;
|
203
|
+
padding:0;
|
204
|
+
margin:0;
|
205
|
+
}
|
206
|
+
#wac-body {
|
207
|
+
position: relative;
|
208
|
+
margin:0;
|
209
|
+
padding:0;
|
210
|
+
width: 100%;
|
211
|
+
height: 100%;
|
212
|
+
overflow: hidden;
|
213
|
+
}
|
214
|
+
#wac-pianoroll {
|
215
|
+
cursor: pointer;
|
216
|
+
margin:0;
|
217
|
+
padding:0;
|
218
|
+
width: 100%;
|
219
|
+
height: 100%;
|
220
|
+
background-size:100% calc(100%*12/16);
|
221
|
+
background-position:left bottom;
|
222
|
+
}
|
223
|
+
#wac-menu {
|
224
|
+
display:none;
|
225
|
+
position:absolute;
|
226
|
+
top:0;
|
227
|
+
left:0;
|
228
|
+
background:#eef;
|
229
|
+
color:#000;
|
230
|
+
padding:2px 10px;
|
231
|
+
border:1px solid #66f;
|
232
|
+
border-radius: 4px;
|
233
|
+
cursor:pointer;
|
234
|
+
}
|
235
|
+
.marker{
|
236
|
+
position: absolute;
|
237
|
+
left:0;
|
238
|
+
top:0;
|
239
|
+
cursor:ew-resize;
|
240
|
+
}
|
241
|
+
#wac-kb{
|
242
|
+
position:absolute;
|
243
|
+
left:0;
|
244
|
+
top:0;
|
245
|
+
width:100px;
|
246
|
+
height:100%;
|
247
|
+
background: repeat-y;
|
248
|
+
background-size:100% calc(100%*12/16);
|
249
|
+
background-position:left bottom;
|
250
|
+
}
|
251
|
+
</style>
|
252
|
+
<div class="wac-body" id="wac-body" >
|
253
|
+
<canvas id="wac-pianoroll" tabindex="0"></canvas>
|
254
|
+
<div id="wac-kb"></div>
|
255
|
+
<img id="wac-markstart" class="marker" src="${this.markstartsrc}" alt=""/>
|
256
|
+
<img id="wac-markend" class="marker" src="${this.markendsrc}" alt=""/>
|
257
|
+
<img id="wac-cursor" class="marker" src="${this.cursorsrc}" alt=""/>
|
258
|
+
<div id="wac-menu">Delete</div>
|
259
|
+
</div>`;
|
260
|
+
|
261
|
+
this.sortSequence = function () {
|
262
|
+
this.sequence.sort((x, y) => {
|
263
|
+
return x.t - y.t;
|
264
|
+
});
|
265
|
+
};
|
266
|
+
this.findNextEv = function (tick) {
|
267
|
+
for (let i = 0; i < this.sequence.length; ++i) {
|
268
|
+
const nev = this.sequence[i];
|
269
|
+
if (nev.t >= this.markend)
|
270
|
+
return {t1: tick, n2: this.markend, dt: this.markend - tick, i: -1};
|
271
|
+
if (nev.t >= tick)
|
272
|
+
return {t1: tick, t2: nev.t, dt: nev.t - tick, i: i};
|
273
|
+
}
|
274
|
+
return {t1: tick, t2: this.markend, dt: this.markend - tick, i: -1};
|
275
|
+
};
|
276
|
+
this.locate = function (tick) {
|
277
|
+
this.cursor = tick;
|
278
|
+
};
|
279
|
+
this.updateTimer = function () {
|
280
|
+
this.tick2time = 4 * 60 / this.tempo / this.timebase;
|
281
|
+
};
|
282
|
+
this.play = function (playcallback, tick) {
|
283
|
+
if (typeof (tick) != "undefined") {
|
284
|
+
this.locate(tick);
|
285
|
+
}
|
286
|
+
if (this.timer != null) {
|
287
|
+
return;
|
288
|
+
}
|
289
|
+
|
290
|
+
this.playcallback = playcallback;
|
291
|
+
this.timestack = [];
|
292
|
+
this.time0 = this.time1 = performance.now() / 1000 + 0.1;
|
293
|
+
this.tick0 = this.tick1 = this.cursor;
|
294
|
+
this.tick2time = 4 * 60 / this.tempo / this.timebase;
|
295
|
+
const p = this.findNextEv(this.cursor);
|
296
|
+
this.index1 = p.i;
|
297
|
+
this.timestack.push([0, this.cursor, 0]);
|
298
|
+
this.timestack.push([this.time0, this.cursor, this.tick2time]);
|
299
|
+
this.time1 += p.dt * this.tick2time;
|
300
|
+
if (p.i < 0) {
|
301
|
+
this.timestack.push([this.time1, this.markstart, this.tick2time]);
|
302
|
+
} else {
|
303
|
+
this.timestack.push([this.time1, p.t1, this.tick2time]);
|
304
|
+
}
|
305
|
+
|
306
|
+
const frameRate = 1000 / 40; // 25ms, equivalent to 40 FPS
|
307
|
+
let lastTime = performance.now();
|
308
|
+
|
309
|
+
|
310
|
+
const playLoop = () => {
|
311
|
+
const currentTime = performance.now();
|
312
|
+
const deltaTime = currentTime - lastTime;
|
313
|
+
|
314
|
+
if (deltaTime >= frameRate) {
|
315
|
+
const current = performance.now() / 1000;
|
316
|
+
|
317
|
+
while (this.timestack.length > 1 && current >= this.timestack[1][0]) {
|
318
|
+
this.timestack.shift();
|
319
|
+
}
|
320
|
+
|
321
|
+
this.cursor = this.timestack[0][1] + (current - this.timestack[0][0]) / this.timestack[0][2];
|
322
|
+
this.redrawMarker();
|
323
|
+
|
324
|
+
while (current + this.preload >= this.time1) {
|
325
|
+
this.time0 = this.time1;
|
326
|
+
this.tick0 = this.tick1;
|
327
|
+
let e = this.sequence[this.index1];
|
328
|
+
|
329
|
+
if (!e || e.t >= this.markend) {
|
330
|
+
this.timestack.push([this.time1, this.markstart, this.tick2time]);
|
331
|
+
const p = this.findNextEv(this.markstart);
|
332
|
+
this.time1 += p.dt * this.tick2time;
|
333
|
+
this.index1 = p.i;
|
334
|
+
} else {
|
335
|
+
this.tick1 = e.t;
|
336
|
+
this.timestack.push([this.time1, e.t, this.tick2time]);
|
337
|
+
let gmax = Math.min(e.t + e.g, this.markend) - e.t;
|
338
|
+
|
339
|
+
if (this.editmode == "gridmono" || this.editmode == "gridpoly") {
|
340
|
+
gmax *= this.gridnoteratio;
|
341
|
+
}
|
342
|
+
|
343
|
+
const cbev = {t: this.time1, g: this.time1 + gmax * this.tick2time, n: e.n};
|
344
|
+
|
345
|
+
if (this.playcallback) {
|
346
|
+
this.playcallback(cbev);
|
347
|
+
}
|
348
|
+
|
349
|
+
e = this.sequence[++this.index1];
|
350
|
+
|
351
|
+
if (!e || e.t >= this.markend) {
|
352
|
+
this.time1 += (this.markend - this.tick1) * this.tick2time;
|
353
|
+
const p = this.findNextEv(this.markstart);
|
354
|
+
this.timestack.push([this.time1, this.markstart, this.tick2time]);
|
355
|
+
this.time1 += p.dt * this.tick2time;
|
356
|
+
this.index1 = p.i;
|
357
|
+
} else {
|
358
|
+
this.time1 += (e.t - this.tick1) * this.tick2time;
|
359
|
+
}
|
360
|
+
}
|
361
|
+
}
|
362
|
+
|
363
|
+
lastTime = currentTime;
|
364
|
+
}
|
365
|
+
|
366
|
+
this.timer = requestAnimationFrame(playLoop);
|
367
|
+
};
|
368
|
+
|
369
|
+
|
370
|
+
this.timer = requestAnimationFrame(playLoop);
|
371
|
+
|
372
|
+
this.stop = function () {
|
373
|
+
if (this.timer) {
|
374
|
+
cancelAnimationFrame(this.timer);
|
375
|
+
this.timer = null;
|
376
|
+
}
|
377
|
+
};
|
378
|
+
|
379
|
+
};
|
380
|
+
this.stop = function () {
|
381
|
+
|
382
|
+
if (this.timer)
|
383
|
+
clearInterval(this.timer);
|
384
|
+
this.timer = null;
|
385
|
+
};
|
386
|
+
this.setMMLString = function (s) {
|
387
|
+
this.sequence = [];
|
388
|
+
let i, l, n, t, defo, defl, tie, evlast;
|
389
|
+
const parse = {s: s, i: i, tb: this.timebase};
|
390
|
+
|
391
|
+
function getNum(p) {
|
392
|
+
var n = 0;
|
393
|
+
while (p.s[p.i] >= "0" && p.s[p.i] <= "9") {
|
394
|
+
n = n * 10 + parseInt(p.s[p.i]);
|
395
|
+
++p.i;
|
396
|
+
}
|
397
|
+
return n;
|
398
|
+
}
|
399
|
+
|
400
|
+
function getLen(p) {
|
401
|
+
var n = getNum(p);
|
402
|
+
if (n == 0)
|
403
|
+
n = defl;
|
404
|
+
n = p.tb / n;
|
405
|
+
var n2 = n;
|
406
|
+
while (p.s[p.i] == ".") {
|
407
|
+
++p.i;
|
408
|
+
n += (n2 >>= 1);
|
409
|
+
}
|
410
|
+
return n;
|
411
|
+
}
|
412
|
+
|
413
|
+
function getNote(p) {
|
414
|
+
switch (p.s[p.i]) {
|
415
|
+
case "c":
|
416
|
+
case "C":
|
417
|
+
n = 0;
|
418
|
+
break;
|
419
|
+
case "d":
|
420
|
+
case "D":
|
421
|
+
n = 2;
|
422
|
+
break;
|
423
|
+
case "e":
|
424
|
+
case "E":
|
425
|
+
n = 4;
|
426
|
+
break;
|
427
|
+
case "f":
|
428
|
+
case "F":
|
429
|
+
n = 5;
|
430
|
+
break;
|
431
|
+
case "g":
|
432
|
+
case "G":
|
433
|
+
n = 7;
|
434
|
+
break;
|
435
|
+
case "a":
|
436
|
+
case "A":
|
437
|
+
n = 9;
|
438
|
+
break;
|
439
|
+
case "b":
|
440
|
+
case "B":
|
441
|
+
n = 11;
|
442
|
+
break;
|
443
|
+
default:
|
444
|
+
n = -1;
|
445
|
+
}
|
446
|
+
++p.i;
|
447
|
+
if (n < 0)
|
448
|
+
return -1;
|
449
|
+
for (; ;) {
|
450
|
+
switch (p.s[p.i]) {
|
451
|
+
case "-":
|
452
|
+
--n;
|
453
|
+
break;
|
454
|
+
case "+":
|
455
|
+
++n;
|
456
|
+
break;
|
457
|
+
case "#":
|
458
|
+
++n;
|
459
|
+
break;
|
460
|
+
default:
|
461
|
+
return n;
|
462
|
+
}
|
463
|
+
++p.i;
|
464
|
+
}
|
465
|
+
}
|
466
|
+
|
467
|
+
defo = 4;
|
468
|
+
defl = 8;
|
469
|
+
t = 0;
|
470
|
+
tie = 0;
|
471
|
+
evlast = null;
|
472
|
+
for (parse.i = 0; parse.i < parse.s.length;) {
|
473
|
+
switch (parse.s[parse.i]) {
|
474
|
+
case '>':
|
475
|
+
++parse.i;
|
476
|
+
++defo;
|
477
|
+
n = -1;
|
478
|
+
l = 0;
|
479
|
+
break;
|
480
|
+
case '<':
|
481
|
+
++parse.i;
|
482
|
+
--defo;
|
483
|
+
n = -1;
|
484
|
+
l = 0;
|
485
|
+
break;
|
486
|
+
case '&':
|
487
|
+
case '^':
|
488
|
+
++parse.i;
|
489
|
+
tie = 1;
|
490
|
+
n = -1;
|
491
|
+
l = 0;
|
492
|
+
break;
|
493
|
+
case 't':
|
494
|
+
case 'T':
|
495
|
+
++parse.i;
|
496
|
+
n = -1;
|
497
|
+
l = 0;
|
498
|
+
this.tempo = getNum(parse);
|
499
|
+
break;
|
500
|
+
case 'o':
|
501
|
+
case 'O':
|
502
|
+
++parse.i;
|
503
|
+
n = -1;
|
504
|
+
l = 0;
|
505
|
+
defo = getNum(parse);
|
506
|
+
break;
|
507
|
+
case 'l':
|
508
|
+
case 'L':
|
509
|
+
++parse.i;
|
510
|
+
n = -1;
|
511
|
+
l = 0;
|
512
|
+
defl = getNum(parse);
|
513
|
+
break;
|
514
|
+
case 'r':
|
515
|
+
case 'R':
|
516
|
+
++parse.i;
|
517
|
+
n = -1;
|
518
|
+
l = getLen(parse);
|
519
|
+
break;
|
520
|
+
default:
|
521
|
+
n = getNote(parse);
|
522
|
+
if (n >= 0)
|
523
|
+
l = getLen(parse);
|
524
|
+
else
|
525
|
+
l = 0;
|
526
|
+
break;
|
527
|
+
}
|
528
|
+
if (n >= 0) {
|
529
|
+
n = (defo - this.octadj) * 12 + n;
|
530
|
+
if (tie && evlast && evlast.n == n) {
|
531
|
+
evlast.g += l;
|
532
|
+
tie = 0;
|
533
|
+
} else
|
534
|
+
this.sequence.push(evlast = {t: t, n: n, g: l, f: 0});
|
535
|
+
}
|
536
|
+
t += l;
|
537
|
+
}
|
538
|
+
this.redraw();
|
539
|
+
};
|
540
|
+
this.getMMLString = function () {
|
541
|
+
function makeNote(n, l, tb) {
|
542
|
+
var mmlnote = "";
|
543
|
+
var ltab = [
|
544
|
+
[960, "1"], [840, "2.."], [720, "2."], [480, "2"],
|
545
|
+
[420, "4.."], [360, "4."], [240, "4"],
|
546
|
+
[210, "8.."], [180, "8."], [120, ""],
|
547
|
+
[105, "16.."], [90, "16."], [60, "16"],
|
548
|
+
[45, "32."], [30, "32"], [16, "60"], [15, "64"],
|
549
|
+
[8, "120"], [4, "240"], [2, "480"], [1, "960"]
|
550
|
+
];
|
551
|
+
l = l * 960 / tb;
|
552
|
+
while (l > 0) {
|
553
|
+
for (let j = 0; j < ltab.length; ++j) {
|
554
|
+
while (l >= ltab[j][0]) {
|
555
|
+
l -= ltab[j][0];
|
556
|
+
mmlnote += "&" + n + ltab[j][1];
|
557
|
+
}
|
558
|
+
}
|
559
|
+
}
|
560
|
+
return mmlnote.substring(1);
|
561
|
+
}
|
562
|
+
|
563
|
+
var mml = "t" + this.tempo + "o4l8";
|
564
|
+
var ti = 0, meas = 0, oct = 5, n;
|
565
|
+
var notes = ["c", "d-", "d", "e-", "e", "f", "g-", "g", "a-", "a", "b-", "b"];
|
566
|
+
for (let i = 0; i < this.sequence.length; ++i) {
|
567
|
+
var ev = this.sequence[i];
|
568
|
+
if (ev.t > ti) {
|
569
|
+
var l = ev.t - ti;
|
570
|
+
mml += makeNote("r", l, this.timebase);
|
571
|
+
ti = ev.t;
|
572
|
+
}
|
573
|
+
var n = ev.n;
|
574
|
+
if (n < oct * 12 || n >= oct * 12 + 12) {
|
575
|
+
oct = (n / 12) | 0;
|
576
|
+
mml += "o" + (oct + this.octadj);
|
577
|
+
}
|
578
|
+
n = notes[n % 12];
|
579
|
+
var l = ev.g;
|
580
|
+
if (i + 1 < this.sequence.length) {
|
581
|
+
var ev2 = this.sequence[i + 1];
|
582
|
+
if (ev2.t < ev.t + l) {
|
583
|
+
l = ev2.t - ev.t;
|
584
|
+
ti = ev2.t;
|
585
|
+
} else
|
586
|
+
ti = ev.t + ev.g;
|
587
|
+
} else
|
588
|
+
ti = ev.t + ev.g;
|
589
|
+
mml += makeNote(n, l, this.timebase);
|
590
|
+
}
|
591
|
+
return mml;
|
592
|
+
};
|
593
|
+
this.hitTest = function (pos) {
|
594
|
+
const ht = {t: 0, n: 0, i: -1, m: " "};
|
595
|
+
const l = this.sequence.length;
|
596
|
+
if (pos.t == this.menu) {
|
597
|
+
ht.m = "m";
|
598
|
+
return ht;
|
599
|
+
}
|
600
|
+
ht.t = (this.xoffset + (pos.x - this.yruler - this.kbwidth) / this.swidth * this.xrange);
|
601
|
+
ht.n = this.yoffset - (pos.y - this.height) / this.steph;
|
602
|
+
if (pos.y >= this.height || pos.x >= this.width) {
|
603
|
+
return ht;
|
604
|
+
}
|
605
|
+
if (pos.y < this.xruler) {
|
606
|
+
ht.m = "x";
|
607
|
+
return ht;
|
608
|
+
}
|
609
|
+
if (pos.x < this.yruler + this.kbwidth) {
|
610
|
+
ht.m = "y";
|
611
|
+
return ht;
|
612
|
+
}
|
613
|
+
for (let i = 0; i < l; ++i) {
|
614
|
+
const ev = this.sequence[i];
|
615
|
+
if ((ht.n | 0) == ev.n) {
|
616
|
+
if (ev.f && Math.abs(ev.t - ht.t) * this.stepw < 8) {
|
617
|
+
ht.m = "B";
|
618
|
+
ht.i = i;
|
619
|
+
return ht;
|
620
|
+
}
|
621
|
+
if (ev.f && Math.abs(ev.t + ev.g - ht.t) * this.stepw < 8) {
|
622
|
+
ht.m = "E";
|
623
|
+
ht.i = i;
|
624
|
+
return ht;
|
625
|
+
}
|
626
|
+
if (ht.t >= ev.t && ht.t < ev.t + ev.g) {
|
627
|
+
ht.i = i;
|
628
|
+
if (this.sequence[i].f)
|
629
|
+
ht.m = "N";
|
630
|
+
else
|
631
|
+
ht.m = "n";
|
632
|
+
return ht;
|
633
|
+
}
|
634
|
+
}
|
635
|
+
}
|
636
|
+
ht.m = "s";
|
637
|
+
return ht;
|
638
|
+
};
|
639
|
+
this.applyTexture = function (ev) {
|
640
|
+
if (this.noteTexture && this.noteTexture.complete) {
|
641
|
+
const w = ev.g * this.stepw;
|
642
|
+
const x = (ev.t - this.xoffset) * this.stepw + this.yruler + this.kbwidth;
|
643
|
+
const y = this.height - (ev.n - this.yoffset) * this.steph;
|
644
|
+
const x2 = (x + w) | 0;
|
645
|
+
const y2 = (y - this.steph) | 0;
|
646
|
+
|
647
|
+
if (ev.f) {
|
648
|
+
this.ctx.fillStyle = this.colnotesel; // green if selected
|
649
|
+
} else {
|
650
|
+
this.ctx.fillStyle = this.colnote; // red if not selected
|
651
|
+
}
|
652
|
+
this.ctx.fillRect(x, y2, x2 - x, y - y2);
|
653
|
+
|
654
|
+
this.ctx.globalAlpha = 0.5; // texture opacity
|
655
|
+
this.ctx.drawImage(this.noteTexture, x, y2, x2 - x, y - y2);
|
656
|
+
this.ctx.globalAlpha = 1.0; // réinitialiser l'opacité
|
657
|
+
ev.textureApplied = true;
|
658
|
+
} else {
|
659
|
+
console.warn('noteTexture is not yet loaded or initialized.');
|
660
|
+
}
|
661
|
+
};
|
662
|
+
|
663
|
+
this.addNote = function (t, n, g, v, f, type = 'note', details = {}) {
|
664
|
+
if (t >= 0 && n >= 0 && n < 128) {
|
665
|
+
const id = this.noteIdCounter++;
|
666
|
+
const ev = {id: id, t: t, n: n, g: g, v: v, f: f, type: type, details: details};
|
667
|
+
console.log('programatic note creation : ' + id + ' type: ' + type + ', details' + details);
|
668
|
+
|
669
|
+
if (!this.noteTexture || !this.noteTexture.complete) {
|
670
|
+
this.noteTexture = new Image();
|
671
|
+
this.noteTexture.src = 'medias/images/waveform.png';
|
672
|
+
this.noteTexture.onload = () => {
|
673
|
+
this.applyTexture(ev);
|
674
|
+
};
|
675
|
+
} else {
|
676
|
+
this.applyTexture(ev);
|
677
|
+
}
|
678
|
+
|
679
|
+
this.sequence.push(ev);
|
680
|
+
this.sortSequence();
|
681
|
+
this.redraw();
|
682
|
+
return ev;
|
683
|
+
}
|
684
|
+
return null;
|
685
|
+
};
|
686
|
+
this.selAreaNote = function (t1, t2, n1, n2) {
|
687
|
+
let t, i = 0, e = this.sequence[i];
|
688
|
+
if (n1 > n2)
|
689
|
+
t = n1, n1 = n2, n2 = t;
|
690
|
+
if (t1 > t2)
|
691
|
+
t = t1, t1 = t2, t2 = t;
|
692
|
+
while (e) {
|
693
|
+
if (e.t >= t1 && e.t < t2 && e.n >= n1 && e.n <= n2)
|
694
|
+
e.f = 1;
|
695
|
+
else
|
696
|
+
e.f = 0;
|
697
|
+
e = this.sequence[++i];
|
698
|
+
}
|
699
|
+
};
|
700
|
+
this.delNote = function (idx) {
|
701
|
+
this.sequence.splice(idx, 1);
|
702
|
+
this.redraw();
|
703
|
+
};
|
704
|
+
this.delAreaNote = function (t, g, n) {
|
705
|
+
const l = this.sequence.length;
|
706
|
+
for (let i = l - 1; i >= 0; --i) {
|
707
|
+
const ev = this.sequence[i];
|
708
|
+
if (typeof (n) != "undefined" && n != i) {
|
709
|
+
if (t <= ev.t && t + g >= ev.t + ev.g) {
|
710
|
+
this.sequence.splice(i, 1);
|
711
|
+
} else if (t <= ev.t && t + g > ev.t && t + g < ev.t + ev.g) {
|
712
|
+
ev.g = ev.t + ev.g - (t + g);
|
713
|
+
ev.t = t + g;
|
714
|
+
} else if (t >= ev.t && t < ev.t + ev.g && t + g >= ev.t + ev.g) {
|
715
|
+
ev.g = t - ev.t;
|
716
|
+
} else if (t > ev.t && t + g < ev.t + ev.g) {
|
717
|
+
this.addNote(t + g, ev.n, ev.t + ev.g - t - g, this.defvelo);
|
718
|
+
ev.g = t - ev.t;
|
719
|
+
}
|
720
|
+
}
|
721
|
+
}
|
722
|
+
};
|
723
|
+
this.delSelectedNote = function () {
|
724
|
+
console.log('deleting note')
|
725
|
+
const l = this.sequence.length;
|
726
|
+
for (let i = l - 1; i >= 0; --i) {
|
727
|
+
const ev = this.sequence[i];
|
728
|
+
if (ev.f)
|
729
|
+
this.sequence.splice(i, 1);
|
730
|
+
}
|
731
|
+
this.refuse = true // to prevent any new note creation when clicking to delete
|
732
|
+
};
|
733
|
+
this.moveSelectedNote = function (dt, dn) {
|
734
|
+
console.log('moving note')
|
735
|
+
const l = this.sequence.length;
|
736
|
+
for (let i = 0; i < l; ++i) {
|
737
|
+
const ev = this.sequence[i];
|
738
|
+
if (ev.f && ev.ot + dt < 0)
|
739
|
+
dt = -ev.ot;
|
740
|
+
}
|
741
|
+
for (let i = 0; i < l; ++i) {
|
742
|
+
const ev = this.sequence[i];
|
743
|
+
if (ev.f) {
|
744
|
+
ev.t = (((ev.ot + dt) / this.snap + .5) | 0) * this.snap;
|
745
|
+
ev.n = ev.on + dn;
|
746
|
+
}
|
747
|
+
}
|
748
|
+
};
|
749
|
+
this.clearSel = function () {
|
750
|
+
const l = this.sequence.length;
|
751
|
+
for (let i = 0; i < l; ++i) {
|
752
|
+
this.sequence[i].f = 0;
|
753
|
+
}
|
754
|
+
};
|
755
|
+
this.selectedNotes = function () {
|
756
|
+
let obj = [];
|
757
|
+
for (let i = this.sequence.length - 1; i >= 0; --i) {
|
758
|
+
const ev = this.sequence[i];
|
759
|
+
if (ev.f)
|
760
|
+
obj.push({i: i, ev: ev, t: ev.t, g: ev.g});
|
761
|
+
}
|
762
|
+
return obj;
|
763
|
+
};
|
764
|
+
this.editDragDown = function (pos) {
|
765
|
+
const ht = this.hitTest(pos);
|
766
|
+
let ev;
|
767
|
+
if (ht.m == "N") {
|
768
|
+
ev = this.sequence[ht.i];
|
769
|
+
this.dragging = {o: "D", m: "N", i: ht.i, t: ht.t, n: ev.n, dt: ht.t - ev.t};
|
770
|
+
for (let i = 0, l = this.sequence.length; i < l; ++i) {
|
771
|
+
ev = this.sequence[i];
|
772
|
+
if (ev.f)
|
773
|
+
ev.on = ev.n, ev.ot = ev.t, ev.og = ev.g;
|
774
|
+
}
|
775
|
+
this.redraw();
|
776
|
+
} else if (ht.m == "n") {
|
777
|
+
ev = this.sequence[ht.i];
|
778
|
+
this.clearSel();
|
779
|
+
ev.f = 1;
|
780
|
+
this.redraw();
|
781
|
+
} else if (ht.m == "E") {
|
782
|
+
this.tool = 'trim_end'
|
783
|
+
const ev = this.sequence[ht.i];
|
784
|
+
console.log('1 note end changed:');
|
785
|
+
|
786
|
+
this.dragging = {o: "D", m: "E", i: ht.i, t: ev.t, g: ev.g, ev: this.selectedNotes()};
|
787
|
+
} else if (ht.m == "B") {
|
788
|
+
this.tool = 'trim_start'
|
789
|
+
const ev = this.sequence[ht.i];
|
790
|
+
console.log('2 note start changed:');
|
791
|
+
|
792
|
+
this.dragging = {o: "D", m: "B", i: ht.i, t: ev.t, g: ev.g, ev: this.selectedNotes()};
|
793
|
+
} else if (ht.m == "s" && ht.t >= 0) {
|
794
|
+
this.clearSel();
|
795
|
+
|
796
|
+
if (this.editing === true && !this.refuse) {
|
797
|
+
var t = ((ht.t / this.snap) | 0) * this.snap;
|
798
|
+
const id = this.noteIdCounter++;
|
799
|
+
console.log('visual note creation : ' + id);
|
800
|
+
var details = {in: 0, out: 0, group: {}};
|
801
|
+
|
802
|
+
const ev = {
|
803
|
+
id: id,
|
804
|
+
t: t,
|
805
|
+
n: ht.n | 0,
|
806
|
+
g: 1,
|
807
|
+
f: 1,
|
808
|
+
type: 'note',
|
809
|
+
details: details,
|
810
|
+
textureApplied: false
|
811
|
+
};
|
812
|
+
this.sequence.push(ev);
|
813
|
+
|
814
|
+
if (!this.noteTexture || !this.noteTexture.complete) {
|
815
|
+
this.noteTexture = new Image();
|
816
|
+
this.noteTexture.src = 'medias/images/waveform.png';
|
817
|
+
this.noteTexture.onload = () => {
|
818
|
+
this.applyTexture(ev);
|
819
|
+
this.redraw();
|
820
|
+
};
|
821
|
+
} else {
|
822
|
+
this.applyTexture(ev);
|
823
|
+
this.redraw();
|
824
|
+
}
|
825
|
+
|
826
|
+
this.dragging = {
|
827
|
+
o: "D",
|
828
|
+
m: "E",
|
829
|
+
i: this.sequence.length - 1,
|
830
|
+
t: t,
|
831
|
+
g: 1,
|
832
|
+
ev: [{t: t, g: 1, ev: this.sequence[this.sequence.length - 1]}]
|
833
|
+
};
|
834
|
+
this.refuse = false;
|
835
|
+
} else {
|
836
|
+
switch (this.downht.m) {
|
837
|
+
case "N":
|
838
|
+
case "B":
|
839
|
+
case "E":
|
840
|
+
console.log('open menu');
|
841
|
+
this.popMenu(this.downpos);
|
842
|
+
this.dragging = {o: "m"};
|
843
|
+
break;
|
844
|
+
default:
|
845
|
+
if (this.editmode == "dragmono" || this.editmode == "dragpoly")
|
846
|
+
this.dragging = {
|
847
|
+
o: "A",
|
848
|
+
p: this.downpos,
|
849
|
+
p2: this.downpos,
|
850
|
+
t1: this.downht.t,
|
851
|
+
n1: this.downht.n
|
852
|
+
};
|
853
|
+
this.refuse = false
|
854
|
+
console.log('===> accept/refuse' + this.refuse)
|
855
|
+
break;
|
856
|
+
}
|
857
|
+
this.canvas.focus();
|
858
|
+
return false;
|
859
|
+
}
|
860
|
+
|
861
|
+
this.redraw();
|
862
|
+
}
|
863
|
+
};
|
864
|
+
this.editDragMove = function (pos) {
|
865
|
+
const ht = this.hitTest(pos);
|
866
|
+
let ev, t;
|
867
|
+
if (this.dragging.o == "D") {
|
868
|
+
switch (this.dragging.m) {
|
869
|
+
case "E":
|
870
|
+
if (this.dragging.ev) {
|
871
|
+
const dt = ((Math.max(0, ht.t) / this.snap + 0.9) | 0) * this.snap - this.dragging.t - this.dragging.g;
|
872
|
+
const list = this.dragging.ev;
|
873
|
+
for (let i = list.length - 1; i >= 0; --i) {
|
874
|
+
const ev = list[i].ev;
|
875
|
+
ev.g = list[i].g + dt;
|
876
|
+
|
877
|
+
if (ev.g <= 0)
|
878
|
+
ev.g = 1;
|
879
|
+
if (this.editmove == "dragmono")
|
880
|
+
this.delAreaNote(ev.t, ev.g);
|
881
|
+
}
|
882
|
+
}
|
883
|
+
this.redraw();
|
884
|
+
break;
|
885
|
+
case "B":
|
886
|
+
if (this.dragging.ev) {
|
887
|
+
const dt = ((Math.max(0, ht.t) / this.snap + 0.9) | 0) * this.snap - this.dragging.t;
|
888
|
+
const list = this.dragging.ev;
|
889
|
+
for (let i = list.length - 1; i >= 0; --i) {
|
890
|
+
const ev = list[i].ev;
|
891
|
+
ev.t = list[i].t + dt;
|
892
|
+
ev.g = list[i].g - dt;
|
893
|
+
|
894
|
+
if (ev.g <= 0)
|
895
|
+
ev.g = 1;
|
896
|
+
if (this.editmove == "dragmono")
|
897
|
+
this.delAreaNote(ev.t, ev.g);
|
898
|
+
}
|
899
|
+
}
|
900
|
+
this.redraw();
|
901
|
+
break;
|
902
|
+
|
903
|
+
ev = this.sequence[this.dragging.i];
|
904
|
+
t = ((Math.max(0, ht.t) / this.snap + 0.5) | 0) * this.snap;
|
905
|
+
ev.g = ev.t + ev.g - t;
|
906
|
+
ev.t = t;
|
907
|
+
if (ev.g < 0) {
|
908
|
+
ev.t += ev.g;
|
909
|
+
ev.g = -ev.g;
|
910
|
+
this.dragging.m = "E";
|
911
|
+
} else if (ev.g == 0) {
|
912
|
+
ev.t = t - 1;
|
913
|
+
ev.g = 1;
|
914
|
+
}
|
915
|
+
this.redraw();
|
916
|
+
break;
|
917
|
+
case "N":
|
918
|
+
this.tool = 'drag'
|
919
|
+
ev = this.sequence[this.dragging.i];
|
920
|
+
console.log('4 note. dragged : ');
|
921
|
+
this.moveSelectedNote((ht.t - this.dragging.t) | 0, (ht.n | 0) - this.dragging.n);
|
922
|
+
this.redraw();
|
923
|
+
break;
|
924
|
+
}
|
925
|
+
}
|
926
|
+
};
|
927
|
+
this.editGridDown = function (pos) {
|
928
|
+
const ht = this.hitTest(pos);
|
929
|
+
if (ht.m == "n") {
|
930
|
+
this.delNote(ht.i);
|
931
|
+
this.dragging = {o: "G", m: "0"};
|
932
|
+
} else if (ht.m == "s" && ht.t >= 0) {
|
933
|
+
const pt = Math.floor(ht.t);
|
934
|
+
if (this.editmode == "gridmono")
|
935
|
+
this.delAreaNote(pt, 1, ht.i);
|
936
|
+
this.addNote(pt, ht.n | 0, 1, this.defvelo);
|
937
|
+
this.dragging = {o: "G", m: "1"};
|
938
|
+
}
|
939
|
+
};
|
940
|
+
this.editGridMove = function (pos) {
|
941
|
+
const ht = this.hitTest(pos);
|
942
|
+
if (this.dragging.o == "G") {
|
943
|
+
switch (this.dragging.m) {
|
944
|
+
case "1":
|
945
|
+
const px = Math.floor(ht.t);
|
946
|
+
if (ht.m == "s") {
|
947
|
+
if (this.editmode == "gridmono")
|
948
|
+
this.delAreaNote(px, 1, ht.i);
|
949
|
+
this.addNote(px, ht.n | 0, 1, this.defvelo);
|
950
|
+
}
|
951
|
+
break;
|
952
|
+
case "0":
|
953
|
+
if (ht.m == "n")
|
954
|
+
this.delNote(ht.i);
|
955
|
+
break;
|
956
|
+
}
|
957
|
+
}
|
958
|
+
};
|
959
|
+
this.setListener = function (el, mode) {
|
960
|
+
this.bindcontextmenu = this.contextmenu.bind(this);
|
961
|
+
this.bindpointermove = this.pointermove.bind(this);
|
962
|
+
this.bindcancel = this.cancel.bind(this);
|
963
|
+
el.addEventListener("mousedown", this.pointerdown.bind(this), true);
|
964
|
+
el.addEventListener("touchstart", this.pointerdown.bind(this), false);
|
965
|
+
if (mode) {
|
966
|
+
el.addEventListener("mouseover", this.pointerover.bind(this), false);
|
967
|
+
el.addEventListener("mouseout", this.pointerout.bind(this), false);
|
968
|
+
}
|
969
|
+
};
|
970
|
+
this.handleKeyboardClick = function (e) {
|
971
|
+
const kbRect = this.kb.getBoundingClientRect();
|
972
|
+
const clickY = e.clientY - kbRect.top;
|
973
|
+
|
974
|
+
const noteNumber = Math.floor(clickY / this.steph);
|
975
|
+
console.log("note to trig :", noteNumber);
|
976
|
+
};
|
977
|
+
this.ready = function () {
|
978
|
+
this.body = root.children[1];
|
979
|
+
this.elem = root.childNodes[2];
|
980
|
+
this.proll = this.elem.children[0];
|
981
|
+
this.canvas = this.elem.children[0];
|
982
|
+
this.kb = this.elem.children[1];
|
983
|
+
this.ctx = this.canvas.getContext("2d");
|
984
|
+
this.kbimg = this.elem.children[1];
|
985
|
+
this.markstartimg = this.elem.children[2];
|
986
|
+
this.markendimg = this.elem.children[3];
|
987
|
+
this.cursorimg = this.elem.children[4];
|
988
|
+
this.menu = this.elem.children[5];
|
989
|
+
this.rcMenu = {x: 0, y: 0, width: 0, height: 0};
|
990
|
+
this.lastx = 0;
|
991
|
+
this.lasty = 0;
|
992
|
+
this.kb.addEventListener('click', this.handleKeyboardClick.bind(this), false);
|
993
|
+
this.canvas.addEventListener('mousemove', this.mousemove.bind(this), false);
|
994
|
+
this.canvas.addEventListener('keydown', this.keydown.bind(this), false);
|
995
|
+
this.canvas.addEventListener('DOMMouseScroll', this.wheel.bind(this), false);
|
996
|
+
this.canvas.addEventListener('mousewheel', this.wheel.bind(this), false);
|
997
|
+
this.setListener(this.canvas, true);
|
998
|
+
this.setListener(this.markendimg, true);
|
999
|
+
this.setListener(this.markstartimg, true);
|
1000
|
+
this.setListener(this.cursorimg, true);
|
1001
|
+
this.setListener(this.menu, false);
|
1002
|
+
this.sequence = [];
|
1003
|
+
this.dragging = {o: null};
|
1004
|
+
this.kbimg.style.height = this.sheight + "px";
|
1005
|
+
this.kbimg.style.backgroundSize = (this.steph * 12) + "px";
|
1006
|
+
this.layout();
|
1007
|
+
this.initialized = 1;
|
1008
|
+
this.redraw();
|
1009
|
+
};
|
1010
|
+
this.setupImage = function () {
|
1011
|
+
};
|
1012
|
+
this.preventScroll = function (e) {
|
1013
|
+
if (e.preventDefault)
|
1014
|
+
e.preventDefault();
|
1015
|
+
};
|
1016
|
+
this.getPos = function (e) {
|
1017
|
+
let t = null;
|
1018
|
+
if (e) {
|
1019
|
+
t = e.target;
|
1020
|
+
this.lastx = e.clientX - this.rcTarget.left;
|
1021
|
+
this.lasty = e.clientY - this.rcTarget.top;
|
1022
|
+
}
|
1023
|
+
if (this.lastx >= this.rcMenu.x && this.lastx < this.rcMenu.x + this.rcMenu.width
|
1024
|
+
&& this.lasty >= this.rcMenu.y && this.lasty < this.rcMenu.y + this.rcMenu.height)
|
1025
|
+
t = this.menu;
|
1026
|
+
return {t: t, x: this.lastx, y: this.lasty};
|
1027
|
+
};
|
1028
|
+
this.contextmenu = function (e) {
|
1029
|
+
e.stopPropagation();
|
1030
|
+
e.preventDefault();
|
1031
|
+
window.removeEventListener("contextmenu", this.bindcontextmenu);
|
1032
|
+
return false;
|
1033
|
+
};
|
1034
|
+
this.keydown = function (e) {
|
1035
|
+
switch (e.keyCode) {
|
1036
|
+
case 8: //delNote using backspace key
|
1037
|
+
this.delSelectedNote();
|
1038
|
+
this.redraw();
|
1039
|
+
break;
|
1040
|
+
}
|
1041
|
+
};
|
1042
|
+
this.popMenu = function (pos) {
|
1043
|
+
console.log('pop menu call from shortcut')
|
1044
|
+
};
|
1045
|
+
this.longtapcountup = function () {
|
1046
|
+
if (++this.longtapcount >= 18) {
|
1047
|
+
clearInterval(this.longtaptimer);
|
1048
|
+
switch (this.downht.m) {
|
1049
|
+
case "N":
|
1050
|
+
case "B":
|
1051
|
+
case "E":
|
1052
|
+
this.popMenu(this.downpos);
|
1053
|
+
this.dragging = {o: "m"};
|
1054
|
+
break;
|
1055
|
+
}
|
1056
|
+
}
|
1057
|
+
};
|
1058
|
+
this.pointerdown = function (ev) {
|
1059
|
+
let e;
|
1060
|
+
if (!this.enable) {
|
1061
|
+
console.log('here 1');
|
1062
|
+
return;
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
if (ev.touches) {
|
1066
|
+
console.log('here 2');
|
1067
|
+
|
1068
|
+
e = ev.touches[0];
|
1069
|
+
} else {
|
1070
|
+
e = ev;
|
1071
|
+
this.rcTarget = this.canvas.getBoundingClientRect();
|
1072
|
+
this.downpos = this.getPos(e);
|
1073
|
+
this.downht = this.hitTest(this.downpos);
|
1074
|
+
|
1075
|
+
if (this.downht.i >= 0) {
|
1076
|
+
let clickedNote = this.sequence[this.downht.i];
|
1077
|
+
let noteId = clickedNote.id;
|
1078
|
+
console.log("Note ID :", noteId + ' note type: ' + clickedNote.type + ', note detail : ' + clickedNote.details);
|
1079
|
+
console.log('-- details below ---')
|
1080
|
+
console.log(clickedNote.details.group)
|
1081
|
+
console.log(clickedNote.details.in)
|
1082
|
+
console.log('-- end details ---')
|
1083
|
+
}
|
1084
|
+
|
1085
|
+
|
1086
|
+
this.longtapcount = 0;
|
1087
|
+
this.longtaptimer = setInterval(this.longtapcountup.bind(this), 100);
|
1088
|
+
window.addEventListener("touchmove", this.bindpointermove, false);
|
1089
|
+
window.addEventListener("mousemove", this.bindpointermove, false);
|
1090
|
+
window.addEventListener("touchend", this.bindcancel);
|
1091
|
+
window.addEventListener("mouseup", this.bindcancel);
|
1092
|
+
window.addEventListener("contextmenu", this.bindcontextmenu);
|
1093
|
+
|
1094
|
+
|
1095
|
+
if (e.button == 2 || e.ctrlKey) {
|
1096
|
+
|
1097
|
+
console.log(' open the menu now!!!')
|
1098
|
+
switch (this.downht.m) {
|
1099
|
+
case "N":
|
1100
|
+
case "B":
|
1101
|
+
case "E":
|
1102
|
+
console.log('open menu');
|
1103
|
+
this.popMenu(this.downpos);
|
1104
|
+
this.dragging = {o: "m"};
|
1105
|
+
break;
|
1106
|
+
default:
|
1107
|
+
if (this.editmode == "dragmono" || this.editmode == "dragpoly")
|
1108
|
+
this.dragging = {
|
1109
|
+
o: "A",
|
1110
|
+
p: this.downpos,
|
1111
|
+
p2: this.downpos,
|
1112
|
+
t1: this.downht.t,
|
1113
|
+
n1: this.downht.n
|
1114
|
+
};
|
1115
|
+
break;
|
1116
|
+
}
|
1117
|
+
ev.preventDefault();
|
1118
|
+
ev.stopPropagation();
|
1119
|
+
this.canvas.focus();
|
1120
|
+
return false;
|
1121
|
+
}
|
1122
|
+
switch (e.target) {
|
1123
|
+
case this.markendimg:
|
1124
|
+
this.dragging = {o: "E", x: this.downpos.x, m: this.markend};
|
1125
|
+
ev.preventDefault();
|
1126
|
+
ev.stopPropagation();
|
1127
|
+
return false;
|
1128
|
+
case this.markstartimg:
|
1129
|
+
this.dragging = {o: "S", x: this.downpos.x, m: this.markstart};
|
1130
|
+
ev.preventDefault();
|
1131
|
+
ev.stopPropagation();
|
1132
|
+
return false;
|
1133
|
+
case this.cursorimg:
|
1134
|
+
this.dragging = {o: "P", x: this.downpos.x, m: this.cursor};
|
1135
|
+
ev.preventDefault();
|
1136
|
+
ev.stopPropagation();
|
1137
|
+
return false;
|
1138
|
+
}
|
1139
|
+
this.dragging = {
|
1140
|
+
o: null,
|
1141
|
+
x: this.downpos.x,
|
1142
|
+
y: this.downpos.y,
|
1143
|
+
offsx: this.xoffset,
|
1144
|
+
offsy: this.yoffset
|
1145
|
+
};
|
1146
|
+
this.canvas.focus();
|
1147
|
+
switch (this.editmode) {
|
1148
|
+
case "gridpoly":
|
1149
|
+
case "gridmono":
|
1150
|
+
this.editGridDown(this.downpos);
|
1151
|
+
break;
|
1152
|
+
case "dragpoly":
|
1153
|
+
case "dragmono":
|
1154
|
+
this.editDragDown(this.downpos);
|
1155
|
+
break;
|
1156
|
+
}
|
1157
|
+
this.press = 1;
|
1158
|
+
if (ev.preventDefault)
|
1159
|
+
ev.preventDefault();
|
1160
|
+
if (ev.stopPropagation)
|
1161
|
+
ev.stopPropagation();
|
1162
|
+
return false;
|
1163
|
+
}
|
1164
|
+
};
|
1165
|
+
this.mousemove = function (e) {
|
1166
|
+
if (this.dragging.o == null) {
|
1167
|
+
this.rcTarget = this.canvas.getBoundingClientRect();
|
1168
|
+
const pos = this.getPos(e);
|
1169
|
+
const ht = this.hitTest(pos);
|
1170
|
+
switch (ht.m) {
|
1171
|
+
case "E":
|
1172
|
+
this.canvas.style.cursor = "e-resize";
|
1173
|
+
break;
|
1174
|
+
case "B":
|
1175
|
+
this.canvas.style.cursor = "w-resize";
|
1176
|
+
break;
|
1177
|
+
case "N":
|
1178
|
+
this.canvas.style.cursor = "move";
|
1179
|
+
break;
|
1180
|
+
case "n":
|
1181
|
+
this.canvas.style.cursor = "pointer";
|
1182
|
+
break;
|
1183
|
+
case "s":
|
1184
|
+
this.canvas.style.cursor = "pointer";
|
1185
|
+
break;
|
1186
|
+
}
|
1187
|
+
}
|
1188
|
+
};
|
1189
|
+
this.pointermove = function (ev) {
|
1190
|
+
let e;
|
1191
|
+
this.rcTarget = this.canvas.getBoundingClientRect();
|
1192
|
+
if (ev.touches)
|
1193
|
+
e = ev.touches[0];
|
1194
|
+
else
|
1195
|
+
e = ev;
|
1196
|
+
if (this.longtaptimer)
|
1197
|
+
clearInterval(this.longtaptimer);
|
1198
|
+
const pos = this.getPos(e);
|
1199
|
+
const ht = this.hitTest(pos);
|
1200
|
+
switch (this.dragging.o) {
|
1201
|
+
case null:
|
1202
|
+
if (this.xscroll)
|
1203
|
+
this.xoffset = this.dragging.offsx + (this.dragging.x - pos.x) * (this.xrange / this.width);
|
1204
|
+
if (this.yscroll)
|
1205
|
+
this.yoffset = this.dragging.offsy + (pos.y - this.dragging.y) * (this.yrange / this.height);
|
1206
|
+
break;
|
1207
|
+
case "m":
|
1208
|
+
if (ht.m == "m") {
|
1209
|
+
this.menu.style.background = "#ff6";
|
1210
|
+
} else {
|
1211
|
+
this.menu.style.background = "#eef";
|
1212
|
+
}
|
1213
|
+
break;
|
1214
|
+
case "A":
|
1215
|
+
this.dragging.p2 = pos;
|
1216
|
+
this.dragging.t2 = ht.t;
|
1217
|
+
this.dragging.n2 = ht.n;
|
1218
|
+
this.redraw();
|
1219
|
+
break;
|
1220
|
+
case "E":
|
1221
|
+
console.log('marker end')
|
1222
|
+
var p = Math.max(1, (this.dragging.m + (pos.x - this.dragging.x) / this.stepw + .5) | 0);
|
1223
|
+
if (this.markstart >= p)
|
1224
|
+
this.markstart = p - 1;
|
1225
|
+
this.markend = p;
|
1226
|
+
break;
|
1227
|
+
case "S":
|
1228
|
+
console.log('marker start')
|
1229
|
+
var p = Math.max(0, (this.dragging.m + (pos.x - this.dragging.x) / this.stepw + .5) | 0);
|
1230
|
+
if (this.markend <= p)
|
1231
|
+
this.markend = p + 1;
|
1232
|
+
this.markstart = p;
|
1233
|
+
break;
|
1234
|
+
case "P":
|
1235
|
+
console.log('playhead')
|
1236
|
+
this.cursor = Math.max(0, (this.dragging.m + (pos.x - this.dragging.x) / this.stepw + .5) | 0);
|
1237
|
+
break;
|
1238
|
+
}
|
1239
|
+
switch (this.editmode) {
|
1240
|
+
case "gridpoly":
|
1241
|
+
case "gridmono":
|
1242
|
+
this.editGridMove(pos);
|
1243
|
+
break;
|
1244
|
+
case "dragpoly":
|
1245
|
+
case "dragmono":
|
1246
|
+
this.editDragMove(pos);
|
1247
|
+
break;
|
1248
|
+
}
|
1249
|
+
ev.preventDefault();
|
1250
|
+
ev.stopPropagation();
|
1251
|
+
return false;
|
1252
|
+
};
|
1253
|
+
this.cancel = function (ev) {
|
1254
|
+
let e;
|
1255
|
+
if (ev.touches)
|
1256
|
+
e = null;
|
1257
|
+
else
|
1258
|
+
e = ev;
|
1259
|
+
if (this.longtaptimer)
|
1260
|
+
clearInterval(this.longtaptimer);
|
1261
|
+
const pos = this.getPos(e);
|
1262
|
+
if (this.dragging.o == "m") {
|
1263
|
+
|
1264
|
+
}
|
1265
|
+
if (this.dragging.o == "A") {
|
1266
|
+
this.selAreaNote(this.dragging.t1, this.dragging.t2, this.dragging.n1, this.dragging.n2);
|
1267
|
+
this.dragging = {o: null};
|
1268
|
+
this.redraw();
|
1269
|
+
}
|
1270
|
+
if (this.editmode == "dragmono") {
|
1271
|
+
for (let ii = this.sequence.length - 1; ii >= 0; --ii) {
|
1272
|
+
const ev = this.sequence[ii];
|
1273
|
+
if (ev && ev.f) {
|
1274
|
+
this.delAreaNote(ev.t, ev.g, ii);
|
1275
|
+
}
|
1276
|
+
}
|
1277
|
+
}
|
1278
|
+
this.redraw();
|
1279
|
+
this.dragging = {o: null};
|
1280
|
+
if (this.press) {
|
1281
|
+
this.sortSequence();
|
1282
|
+
}
|
1283
|
+
this.press = 0;
|
1284
|
+
window.removeEventListener('touchstart', this.preventScroll, false);
|
1285
|
+
window.removeEventListener("mousemove", this.bindpointermove, false);
|
1286
|
+
window.removeEventListener("touchend", this.bindcancel, false);
|
1287
|
+
window.removeEventListener("mouseup", this.bindcancel, false);
|
1288
|
+
ev.preventDefault();
|
1289
|
+
ev.stopPropagation();
|
1290
|
+
return false;
|
1291
|
+
};
|
1292
|
+
this.pointerover = function (e) {
|
1293
|
+
};
|
1294
|
+
this.pointerout = function (e) {
|
1295
|
+
};
|
1296
|
+
this.wheel = function (e) {
|
1297
|
+
let delta = 0;
|
1298
|
+
const pos = this.getPos(e);
|
1299
|
+
if (!e)
|
1300
|
+
e = window.event;
|
1301
|
+
if (e.wheelDelta)
|
1302
|
+
delta = e.wheelDelta / 120;
|
1303
|
+
else if (e.detail)
|
1304
|
+
delta = -e.detail / 3;
|
1305
|
+
const ht = this.hitTest(pos);
|
1306
|
+
if ((this.wheelzoomx || this.wheelzoom) && ht.m == "x") {
|
1307
|
+
if (delta > 0) {
|
1308
|
+
this.xoffset = ht.t - (ht.t - this.xoffset) / 1.2
|
1309
|
+
this.xrange /= 1.2;
|
1310
|
+
} else {
|
1311
|
+
this.xoffset = ht.t - (ht.t - this.xoffset) * 1.2
|
1312
|
+
this.xrange *= 1.2;
|
1313
|
+
}
|
1314
|
+
}
|
1315
|
+
if ((this.wheelzoomy || this.wheelzoom) && ht.m == "y") {
|
1316
|
+
if (delta > 0) {
|
1317
|
+
this.yoffset = ht.n - (ht.n - this.yoffset) / 1.2
|
1318
|
+
this.yrange /= 1.2;
|
1319
|
+
} else {
|
1320
|
+
this.yoffset = ht.n - (ht.n - this.yoffset) * 1.2
|
1321
|
+
this.yrange *= 1.2;
|
1322
|
+
}
|
1323
|
+
|
1324
|
+
}
|
1325
|
+
e.preventDefault();
|
1326
|
+
};
|
1327
|
+
this.layout = function () {
|
1328
|
+
if (typeof (this.kbwidth) == "undefined")
|
1329
|
+
return;
|
1330
|
+
const proll = this.proll;
|
1331
|
+
const bodystyle = this.body.style;
|
1332
|
+
if (this.bgsrc)
|
1333
|
+
proll.style.background = "url('" + this.bgsrc + "')";
|
1334
|
+
this.kbimg.style.background = "url('" + this.kbsrc + "')";
|
1335
|
+
if (this.width) {
|
1336
|
+
proll.width = this.width;
|
1337
|
+
bodystyle.width = proll.style.width = this.width + "px";
|
1338
|
+
}
|
1339
|
+
if (this.height) {
|
1340
|
+
proll.height = this.height;
|
1341
|
+
bodystyle.height = proll.style.height = this.height + "px";
|
1342
|
+
}
|
1343
|
+
this.swidth = proll.width - this.yruler;
|
1344
|
+
this.swidth -= this.kbwidth;
|
1345
|
+
this.sheight = proll.height - this.xruler;
|
1346
|
+
this.redraw();
|
1347
|
+
};
|
1348
|
+
this.redrawMarker = function () {
|
1349
|
+
if (!this.initialized)
|
1350
|
+
return;
|
1351
|
+
const cur = (this.cursor - this.xoffset) * this.stepw + this.yruler + this.kbwidth;
|
1352
|
+
this.cursorimg.style.left = (cur + this.cursoroffset) + "px";
|
1353
|
+
const start = (this.markstart - this.xoffset) * this.stepw + this.yruler + this.kbwidth;
|
1354
|
+
this.markstartimg.style.left = (start + this.markstartoffset) + "px";
|
1355
|
+
const end = (this.markend - this.xoffset) * this.stepw + this.yruler + this.kbwidth;
|
1356
|
+
this.markendimg.style.left = (end + this.markendoffset) + "px";
|
1357
|
+
};
|
1358
|
+
this.redrawGrid = function () {
|
1359
|
+
for (let y = 0; y < 128; ++y) {
|
1360
|
+
if (this.semiflag[y % 12] & 1)
|
1361
|
+
this.ctx.fillStyle = this.coldk;
|
1362
|
+
else
|
1363
|
+
this.ctx.fillStyle = this.collt;
|
1364
|
+
let ys = this.height - (y - this.yoffset) * this.steph;
|
1365
|
+
this.ctx.fillRect(this.yruler + this.kbwidth, ys | 0, this.swidth, -this.steph);
|
1366
|
+
this.ctx.fillStyle = this.colgrid;
|
1367
|
+
this.ctx.fillRect(this.yruler + this.kbwidth, ys | 0, this.swidth, 1);
|
1368
|
+
}
|
1369
|
+
for (let t = 0; ; t += this.grid) {
|
1370
|
+
let x = this.stepw * (t - this.xoffset) + this.yruler + this.kbwidth;
|
1371
|
+
this.ctx.fillRect(x | 0, this.xruler, 1, this.sheight);
|
1372
|
+
if (x >= this.width)
|
1373
|
+
break;
|
1374
|
+
}
|
1375
|
+
};
|
1376
|
+
this.semiflag = [6, 1, 0, 1, 0, 2, 1, 0, 1, 0, 1, 0];
|
1377
|
+
this.redrawXRuler = function () {
|
1378
|
+
if (this.xruler) {
|
1379
|
+
this.ctx.textAlign = "left";
|
1380
|
+
this.ctx.font = (this.xruler / 2) + "px 'sans-serif'";
|
1381
|
+
this.ctx.fillStyle = this.colrulerbg;
|
1382
|
+
this.ctx.fillRect(0, 0, this.width, this.xruler);
|
1383
|
+
this.ctx.fillStyle = this.colrulerborder;
|
1384
|
+
this.ctx.fillRect(0, 0, this.width, 1);
|
1385
|
+
this.ctx.fillRect(0, 0, 1, this.xruler);
|
1386
|
+
this.ctx.fillRect(0, this.xruler - 1, this.width, 1);
|
1387
|
+
this.ctx.fillRect(this.width - 1, 0, 1, this.xruler);
|
1388
|
+
this.ctx.fillStyle = this.colrulerfg;
|
1389
|
+
for (let t = 0; ; t += this.timebase) {
|
1390
|
+
let x = (t - this.xoffset) * this.stepw + this.yruler + this.kbwidth;
|
1391
|
+
this.ctx.fillRect(x, 0, 1, this.xruler);
|
1392
|
+
this.ctx.fillText(t / this.timebase + 1, x + 4, this.xruler - 8);
|
1393
|
+
if (x >= this.width)
|
1394
|
+
break;
|
1395
|
+
}
|
1396
|
+
}
|
1397
|
+
};
|
1398
|
+
this.redrawYRuler = function () {
|
1399
|
+
if (this.yruler) {
|
1400
|
+
this.ctx.textAlign = "right";
|
1401
|
+
this.ctx.font = (this.steph / 2) + "px 'sans-serif'";
|
1402
|
+
this.ctx.fillStyle = this.colrulerbg;
|
1403
|
+
this.ctx.fillRect(0, this.xruler, this.yruler, this.sheight);
|
1404
|
+
this.ctx.fillStyle = this.colrulerborder;
|
1405
|
+
this.ctx.fillRect(0, this.xruler, 1, this.sheight);
|
1406
|
+
this.ctx.fillRect(this.yruler, this.xruler, 1, this.sheight);
|
1407
|
+
this.ctx.fillRect(0, this.height - 1, this.yruler, 1);
|
1408
|
+
this.ctx.fillStyle = this.colrulerfg;
|
1409
|
+
for (let y = 0; y < 128; y += 12) {
|
1410
|
+
const ys = this.height - this.steph * (y - this.yoffset);
|
1411
|
+
this.ctx.fillRect(0, ys | 0, this.yruler, -1);
|
1412
|
+
this.ctx.fillText("C" + (((y / 12) | 0) + this.octadj), this.yruler - 4, ys - 4);
|
1413
|
+
}
|
1414
|
+
}
|
1415
|
+
this.kbimg.style.top = (this.xruler) + "px";
|
1416
|
+
this.kbimg.style.left = this.yruler + "px";
|
1417
|
+
this.kbimg.style.width = this.kbwidth + "px";
|
1418
|
+
this.kbimg.style.backgroundSize = "100% " + (this.steph * 12) + "px";
|
1419
|
+
this.kbimg.style.backgroundPosition = "0px " + (this.sheight + this.steph * this.yoffset) + "px";
|
1420
|
+
};
|
1421
|
+
this.redrawKeyboard = function () {
|
1422
|
+
if (this.yruler) {
|
1423
|
+
this.ctx.textAlign = "right";
|
1424
|
+
this.ctx.font = (this.steph / 2) + "px 'sans-serif'";
|
1425
|
+
this.ctx.fillStyle = this.colortab.kbwh;
|
1426
|
+
this.ctx.fillRect(1, this.xruler, this.yruler, this.sheight);
|
1427
|
+
this.ctx.fillStyle = this.colortab.kbbk;
|
1428
|
+
for (let y = 0; y < 128; ++y) {
|
1429
|
+
const ys = this.height - this.steph * (y - this.yoffset);
|
1430
|
+
const ysemi = y % 12;
|
1431
|
+
const fsemi = this.semiflag[ysemi];
|
1432
|
+
if (fsemi & 1) {
|
1433
|
+
this.ctx.fillRect(0, ys, this.yruler / 2, -this.steph);
|
1434
|
+
this.ctx.fillRect(0, (ys - this.steph / 2) | 0, this.yruler, -1);
|
1435
|
+
}
|
1436
|
+
if (fsemi & 2)
|
1437
|
+
this.ctx.fillRect(0, ys | 0, this.yruler, -1);
|
1438
|
+
if (fsemi & 4)
|
1439
|
+
this.ctx.fillText("C" + (((y / 12) | 0) + this.octadj), this.yruler - 4, ys - 4);
|
1440
|
+
}
|
1441
|
+
this.ctx.fillRect(this.yruler, this.xruler, 1, this.sheight);
|
1442
|
+
}
|
1443
|
+
};
|
1444
|
+
this.redrawAreaSel = function () {
|
1445
|
+
if (this.dragging && this.dragging.o == "A") {
|
1446
|
+
this.ctx.fillStyle = this.colselarea;
|
1447
|
+
this.ctx.fillRect(this.dragging.p.x, this.dragging.p.y, this.dragging.p2.x - this.dragging.p.x, this.dragging.p2.y - this.dragging.p.y);
|
1448
|
+
}
|
1449
|
+
};
|
1450
|
+
this.redraw = function () {
|
1451
|
+
let x, w, y;
|
1452
|
+
if (!this.ctx) return;
|
1453
|
+
|
1454
|
+
this.ctx.clearRect(0, 0, this.width, this.height);
|
1455
|
+
this.stepw = this.swidth / this.xrange;
|
1456
|
+
this.steph = this.sheight / this.yrange;
|
1457
|
+
|
1458
|
+
this.redrawGrid();
|
1459
|
+
|
1460
|
+
const l = this.sequence.length;
|
1461
|
+
for (let s = 0; s < l; ++s) {
|
1462
|
+
const ev = this.sequence[s];
|
1463
|
+
const noteHeight = this.steph;
|
1464
|
+
console.log('tool active: ' + this.tool + ', length : ' + ev.g + ' start: ' + ev.t)
|
1465
|
+
|
1466
|
+
if (ev.f) {
|
1467
|
+
this.ctx.fillStyle = this.colnotesel;
|
1468
|
+
} else {
|
1469
|
+
this.ctx.fillStyle = this.colnote;
|
1470
|
+
}
|
1471
|
+
|
1472
|
+
w = ev.g * this.stepw;
|
1473
|
+
x = (ev.t - this.xoffset) * this.stepw + this.yruler + this.kbwidth;
|
1474
|
+
y = this.height - (ev.n - this.yoffset) * this.steph;
|
1475
|
+
|
1476
|
+
this.ctx.fillRect(x, y - noteHeight, w, noteHeight);
|
1477
|
+
|
1478
|
+
this.applyTexture(ev);
|
1479
|
+
}
|
1480
|
+
|
1481
|
+
this.redrawYRuler();
|
1482
|
+
this.redrawXRuler();
|
1483
|
+
this.redrawMarker();
|
1484
|
+
this.redrawAreaSel();
|
1485
|
+
};
|
1486
|
+
this.ready();
|
1487
|
+
}
|
1488
|
+
|
1489
|
+
getAttr(n, def) {
|
1490
|
+
let v = this.getAttribute(n);
|
1491
|
+
if (v == "" || v == null) return def;
|
1492
|
+
switch (typeof (def)) {
|
1493
|
+
case "number":
|
1494
|
+
if (v == "true") return 1;
|
1495
|
+
v = +v;
|
1496
|
+
if (isNaN(v)) return 0;
|
1497
|
+
return v;
|
1498
|
+
}
|
1499
|
+
return v;
|
1500
|
+
}
|
1501
|
+
});
|
1502
|
+
|
1503
|
+
|
1504
|
+
/// pianoroll creator :
|
1505
|
+
|
1506
|
+
function aRoll(id, target, width, height) {
|
1507
|
+
// we build the pianoroll here
|
1508
|
+
const pianoRoll = document.createElement('webaudio-pianoroll');
|
1509
|
+
|
1510
|
+
pianoRoll.setAttribute('id', id);
|
1511
|
+
pianoRoll.setAttribute('width', width);
|
1512
|
+
pianoRoll.setAttribute('height', height);
|
1513
|
+
|
1514
|
+
const targetElement = document.getElementById(target);
|
1515
|
+
|
1516
|
+
if (targetElement) {
|
1517
|
+
targetElement.appendChild(pianoRoll);
|
1518
|
+
} else {
|
1519
|
+
console.error('Target element not found');
|
1520
|
+
}
|
1521
|
+
}
|
1522
|
+
|
1523
|
+
/// pianoroll builder below
|
1524
|
+
|
1525
|
+
function setTempo(id) {
|
1526
|
+
let pianoRoll = document.getElementById(id);
|
1527
|
+
pianoRoll.tempo = 33;
|
1528
|
+
pianoRoll.updateTimer();
|
1529
|
+
console.log('Tempo:', pianoRoll.tempo);
|
1530
|
+
}
|
1531
|
+
|
1532
|
+
function changeEditMode(id, mode) {
|
1533
|
+
document.getElementById(id).editmode = mode;
|
1534
|
+
}
|
1535
|
+
|
1536
|
+
function AddNote(id) {
|
1537
|
+
let sequence = document.getElementById(id);
|
1538
|
+
sequence.addNote(
|
1539
|
+
0, // Tick
|
1540
|
+
66, // Note
|
1541
|
+
2, // Duration
|
1542
|
+
39, // velocity???
|
1543
|
+
0, // selected or not
|
1544
|
+
'notes' // type (group)
|
1545
|
+
);
|
1546
|
+
}
|
1547
|
+
|
1548
|
+
function setMarkStart(id) {
|
1549
|
+
let sequence = document.getElementById(id);
|
1550
|
+
sequence.markstart = (3)
|
1551
|
+
}
|
1552
|
+
|
1553
|
+
function setMarkEnd(id) {
|
1554
|
+
let sequence = document.getElementById(id);
|
1555
|
+
sequence.markend = (7)
|
1556
|
+
}
|
1557
|
+
|
1558
|
+
function playHead(id) {
|
1559
|
+
let sequence = document.getElementById(id);
|
1560
|
+
sequence.locate(3);
|
1561
|
+
}
|
1562
|
+
|
1563
|
+
function menu(id) {
|
1564
|
+
console.log('open a menu here!!!')
|
1565
|
+
}
|
1566
|
+
|
1567
|
+
function editing(id) {
|
1568
|
+
let sequence = document.getElementById(id);
|
1569
|
+
if (sequence.editing) {
|
1570
|
+
sequence.editing = false
|
1571
|
+
sequence.tool = 'select'
|
1572
|
+
console.log('no editing')
|
1573
|
+
} else {
|
1574
|
+
sequence.editing = true
|
1575
|
+
sequence.tool = 'create'
|
1576
|
+
console.log('editing active')
|
1577
|
+
}
|
1578
|
+
|
1579
|
+
}
|
1580
|
+
|
1581
|
+
function createExtendedNote(notes) {
|
1582
|
+
if (notes.length === 0) {
|
1583
|
+
return null;
|
1584
|
+
}
|
1585
|
+
|
1586
|
+
let selectedNotes = notes.filter(note => note.f === 1);
|
1587
|
+
|
1588
|
+
if (selectedNotes.length === 0) {
|
1589
|
+
return null;
|
1590
|
+
}
|
1591
|
+
|
1592
|
+
let earliestStartNote = selectedNotes.reduce((earliest, note) => note.t < earliest.t ? note : earliest, selectedNotes[0]);
|
1593
|
+
|
1594
|
+
let latestEndNote = selectedNotes.reduce((latest, note) => (note.t + note.g) > (latest.t + latest.g) ? note : latest, selectedNotes[0]);
|
1595
|
+
|
1596
|
+
let newNote = {
|
1597
|
+
id: Math.max(...notes.map(note => note.id)) + 1, // new id build on previous build id
|
1598
|
+
t: earliestStartNote.t, // start of first selected note in timecode
|
1599
|
+
n: earliestStartNote.n, // use the same pitch as the first seledted note
|
1600
|
+
g: (latestEndNote.t + latestEndNote.g) - earliestStartNote.t, // compute total length
|
1601
|
+
f: 1 // by default select the newly created note
|
1602
|
+
};
|
1603
|
+
|
1604
|
+
return newNote;
|
1605
|
+
}
|
1606
|
+
|
1607
|
+
|
1608
|
+
function deleteSelectedNotes(id) {
|
1609
|
+
let pianoroll = document.getElementById(id);
|
1610
|
+
let sequence = pianoroll.sequence;
|
1611
|
+
if (Array.isArray(sequence)) {
|
1612
|
+
pianoroll.sequence = sequence.filter(note => note.f !== 1);
|
1613
|
+
if (typeof pianoroll.redraw === 'function') {
|
1614
|
+
pianoroll.redraw();
|
1615
|
+
}
|
1616
|
+
}
|
1617
|
+
}
|
1618
|
+
|
1619
|
+
function group(id) {
|
1620
|
+
let sequence = document.getElementById(id);
|
1621
|
+
let notes = sequence.sequence;
|
1622
|
+
let newNote = createExtendedNote(notes)
|
1623
|
+
let noteToDel = [];
|
1624
|
+
notes.forEach(note => {
|
1625
|
+
if (note.f === 1) {
|
1626
|
+
noteToDel.push(note)
|
1627
|
+
}
|
1628
|
+
});
|
1629
|
+
noteToDel.forEach(note => {
|
1630
|
+
const index = sequence.sequence.indexOf(note);
|
1631
|
+
if (index !== -1) {
|
1632
|
+
sequence.sequence.splice(index, 1);
|
1633
|
+
}
|
1634
|
+
});
|
1635
|
+
sequence.addNote(newNote.t, 60, newNote.g, 8, 1, 'group', {in: 0, out: 0, group: noteToDel});
|
1636
|
+
}
|
1637
|
+
|
1638
|
+
function notes(id) {
|
1639
|
+
let sequence = document.getElementById(id);
|
1640
|
+
let notes = sequence.sequence;
|
1641
|
+
console.log(notes)
|
1642
|
+
|
1643
|
+
}
|
1644
|
+
|
1645
|
+
function selectAll(id) {
|
1646
|
+
let pianoroll = document.getElementById(id);
|
1647
|
+
pianoroll.sequence.forEach(note => {
|
1648
|
+
note.f = 1;
|
1649
|
+
});
|
1650
|
+
pianoroll.redraw();
|
1651
|
+
}
|
1652
|
+
|
1653
|
+
function deSelectAll(id) {
|
1654
|
+
let pianoroll = document.getElementById(id);
|
1655
|
+
pianoroll.sequence.forEach(note => {
|
1656
|
+
note.f = 0;
|
1657
|
+
});
|
1658
|
+
pianoroll.redraw();
|
1659
|
+
}
|
1660
|
+
|
1661
|
+
function marker(id) {
|
1662
|
+
const pianoRoll = document.getElementById(id);
|
1663
|
+
pianoRoll.marker(12, 'playheadID1', 'My First Playhead');
|
1664
|
+
}
|
1665
|
+
|
1666
|
+
function removeMarker(id) {
|
1667
|
+
const pianoRoll = document.getElementById(id);
|
1668
|
+
pianoRoll.removeMarker('playheadID1');
|
1669
|
+
}
|
1670
|
+
|
1671
|
+
///
|
1672
|
+
|
1673
|
+
function clear_now() {
|
1674
|
+
console.clear()
|
1675
|
+
}
|
1676
|
+
|