forked from jummbus/jummbox
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEnvelopeEditor.ts
More file actions
141 lines (121 loc) · 6.66 KB
/
EnvelopeEditor.ts
File metadata and controls
141 lines (121 loc) · 6.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (c) 2012-2022 John Nesky and contributing authors, distributed under the MIT license, see accompanying the LICENSE.md file.
import {InstrumentType, Config} from "../synth/SynthConfig";
import {Instrument} from "../synth/synth";
import {SongDocument} from "./SongDocument";
import {ChangeSetEnvelopeTarget, ChangeSetEnvelopeType, ChangeRemoveEnvelope} from "./changes";
import {HTML} from "imperative-html/dist/esm/elements-strict";
export class EnvelopeEditor {
public readonly container: HTMLElement = HTML.div({class: "envelopeEditor"});
private readonly _rows: HTMLDivElement[] = [];
private readonly _targetSelects: HTMLSelectElement[] = [];
private readonly _envelopeSelects: HTMLSelectElement[] = [];
private readonly _deleteButtons: HTMLButtonElement[] = [];
private _renderedEnvelopeCount: number = 0;
private _renderedEqFilterCount: number = -1;
private _renderedNoteFilterCount: number = -1;
private _renderedInstrumentType: InstrumentType;
private _renderedEffects: number = 0;
constructor(private _doc: SongDocument) {
this.container.addEventListener("change", this._onChange);
this.container.addEventListener("click", this._onClick);
}
private _onChange = (event: Event): void => {
const targetSelectIndex: number = this._targetSelects.indexOf(<any> event.target);
const envelopeSelectIndex: number = this._envelopeSelects.indexOf(<any> event.target);
if (targetSelectIndex != -1) {
const combinedValue: number = parseInt(this._targetSelects[targetSelectIndex].value);
const target: number = combinedValue % Config.instrumentAutomationTargets.length;
const index: number = (combinedValue / Config.instrumentAutomationTargets.length) >>> 0;
this._doc.record(new ChangeSetEnvelopeTarget(this._doc, targetSelectIndex, target, index));
} else if (envelopeSelectIndex != -1) {
this._doc.record(new ChangeSetEnvelopeType(this._doc, envelopeSelectIndex, this._envelopeSelects[envelopeSelectIndex].selectedIndex));
}
}
private _onClick = (event: MouseEvent): void => {
const index: number = this._deleteButtons.indexOf(<any> event.target);
if (index != -1) {
this._doc.record(new ChangeRemoveEnvelope(this._doc, index));
}
}
private _makeOption(target: number, index: number): HTMLOptionElement {
let displayName = Config.instrumentAutomationTargets[target].displayName;
if (Config.instrumentAutomationTargets[target].maxCount > 1) {
if (displayName.indexOf("#") != -1) {
displayName = displayName.replace("#", String(index+1));
} else {
displayName += " " + (index+1);
}
}
return HTML.option({value: target + index * Config.instrumentAutomationTargets.length}, displayName);
}
private _updateTargetOptionVisibility(menu: HTMLSelectElement, instrument: Instrument): void {
for (let optionIndex: number = 0; optionIndex < menu.childElementCount; optionIndex++) {
const option: HTMLOptionElement = <HTMLOptionElement> menu.children[optionIndex];
const combinedValue: number = parseInt(option.value);
const target: number = combinedValue % Config.instrumentAutomationTargets.length;
const index: number = (combinedValue / Config.instrumentAutomationTargets.length) >>> 0;
option.hidden = !instrument.supportsEnvelopeTarget(target, index);
}
}
public render(): void {
const instrument: Instrument = this._doc.song.channels[this._doc.channel].instruments[this._doc.getCurrentInstrument()];
for (let envelopeIndex: number = this._rows.length; envelopeIndex < instrument.envelopeCount; envelopeIndex++) {
const targetSelect: HTMLSelectElement = HTML.select();
for (let target: number = 0; target < Config.instrumentAutomationTargets.length; target++) {
const interleaved: boolean = (Config.instrumentAutomationTargets[target].interleave);
for (let index: number = 0; index < Config.instrumentAutomationTargets[target].maxCount; index++) {
targetSelect.appendChild(this._makeOption(target, index));
if (interleaved) {
targetSelect.appendChild(this._makeOption(target + 1, index));
}
}
if (interleaved) target++;
}
const envelopeSelect: HTMLSelectElement = HTML.select();
for (let envelope: number = 0; envelope < Config.envelopes.length; envelope++) {
envelopeSelect.appendChild(HTML.option({value: envelope}, Config.envelopes[envelope].name));
}
const deleteButton: HTMLButtonElement = HTML.button({type: "button", class: "delete-envelope"});
const row: HTMLDivElement = HTML.div({class: "envelope-row"},
HTML.div({class: "selectContainer", style: "width: 0; flex: 1;"}, targetSelect),
HTML.div({class: "selectContainer", style: "width: 0; flex: 0.7;"}, envelopeSelect),
deleteButton,
);
this.container.appendChild(row);
this._rows[envelopeIndex] = row;
this._targetSelects[envelopeIndex] = targetSelect;
this._envelopeSelects[envelopeIndex] = envelopeSelect;
this._deleteButtons[envelopeIndex] = deleteButton;
}
for (let envelopeIndex: number = this._renderedEnvelopeCount; envelopeIndex < instrument.envelopeCount; envelopeIndex++) {
this._rows[envelopeIndex].style.display = "";
// For newly visible rows, update target option visibiliy.
this._updateTargetOptionVisibility(this._targetSelects[envelopeIndex], instrument);
}
for (let envelopeIndex: number = instrument.envelopeCount; envelopeIndex < this._renderedEnvelopeCount; envelopeIndex++) {
this._rows[envelopeIndex].style.display = "none";
}
let useControlPointCount: number = instrument.noteFilter.controlPointCount;
if (instrument.noteFilterType)
useControlPointCount = 1;
if (this._renderedEqFilterCount != instrument.eqFilter.controlPointCount ||
this._renderedNoteFilterCount != useControlPointCount ||
this._renderedInstrumentType != instrument.type ||
this._renderedEffects != instrument.effects)
{
// Update target option visibility for previously visible rows.
for (let envelopeIndex: number = 0; envelopeIndex < this._renderedEnvelopeCount; envelopeIndex++) {
this._updateTargetOptionVisibility(this._targetSelects[envelopeIndex], instrument);
}
}
for (let envelopeIndex: number = 0; envelopeIndex < instrument.envelopeCount; envelopeIndex++) {
this._targetSelects[envelopeIndex].value = String(instrument.envelopes[envelopeIndex].target + instrument.envelopes[envelopeIndex].index * Config.instrumentAutomationTargets.length);
this._envelopeSelects[envelopeIndex].selectedIndex = instrument.envelopes[envelopeIndex].envelope;
}
this._renderedEnvelopeCount = instrument.envelopeCount;
this._renderedEqFilterCount = instrument.eqFilter.controlPointCount;
this._renderedNoteFilterCount = useControlPointCount;
this._renderedInstrumentType = instrument.type;
this._renderedEffects = instrument.effects;
}
}