Skip to content

Commit ebefc4c

Browse files
committed
add midi cc to patch interface
1 parent bab7fe0 commit ebefc4c

File tree

5 files changed

+111
-37
lines changed

5 files changed

+111
-37
lines changed

src/app/components/metronome/index.js

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,48 +12,27 @@ const metronome = metronomeManager.getMetronome();
1212
const titleSymbols = [ '\\', '|', '/', '-', ];
1313

1414
class Metronome extends BaseComponent {
15-
1615
constructor() {
17-
super(style, markup);
16+
super(style, markup, ['metronomeButton', 'metronomeInput']);
1817
this.isRunning = false;
19-
this.metronomeButton = this.shadowRoot.getElementById('metronome-button');
20-
this.input = this.shadowRoot.getElementById('metronome-input');
21-
this.input.value = metronome.getTempo();
22-
this.input.addEventListener('change', $event => {
23-
const value = parseInt(this.input.value);
18+
this.dom.metronomeInput.value = metronome.getTempo();
19+
this.dom.metronomeInput.addEventListener('change', $event => {
20+
const value = parseInt(this.dom.metronomeInput.value);
2421
metronome.setTempo(value);
2522
});
2623
this.titleIndex = 0;
2724
}
2825

2926
connectedCallback() {
3027
this.titleElement = document.getElementsByTagName('title')[0];
31-
// console.log('titleElement', titleElement);
32-
3328
window.addEventListener('keydown', $event => {
3429
if ($event.code !== 'Space') { return; }
3530
$event.preventDefault();
3631
$event.stopPropagation();
37-
this.metronomeButton.click();
32+
this.dom.metronomeButton.click();
3833
});
39-
40-
const schedulable = {
41-
processTick: (tickNumber, time) => {},
42-
render: (beatNumber, lastBeatNumber) => {
43-
graphicsChannel.postMessage({
44-
type: 'TICK',
45-
beatNumber,
46-
lastBeatNumber
47-
});
48-
if (beatNumber % 8 !== 0) { return; }
49-
const symbol = titleSymbols[ this.titleIndex++ % titleSymbols.length ];
50-
this.titleElement.innerText = `${symbol} ${symbol} ${symbol}`;
51-
},
52-
start: () => {},
53-
stop: () => {}
54-
};
55-
const scheduler = metronomeManager.getScheduler();
56-
scheduler.register(schedulable);
34+
this.schedulable = this.buildSchedulable();
35+
metronomeManager.getScheduler().register(this.schedulable);
5736
}
5837

5938
onMetronomeClick() {
@@ -69,8 +48,26 @@ class Metronome extends BaseComponent {
6948

7049
disconnectedCallback() {
7150
metronome.stop();
51+
metronomeManager.getScheduler().deregister(this.schedulable);
7252
};
7353

54+
buildSchedulable() {
55+
return {
56+
processTick: (tickNumber, time) => {},
57+
render: (beatNumber, lastBeatNumber) => {
58+
graphicsChannel.postMessage({
59+
type: 'TICK',
60+
beatNumber,
61+
lastBeatNumber
62+
});
63+
if (beatNumber % 8 !== 0) { return; }
64+
const symbol = titleSymbols[ this.titleIndex++ % titleSymbols.length ];
65+
this.titleElement.innerText = `${symbol} ${symbol} ${symbol}`;
66+
},
67+
start: () => {},
68+
stop: () => {}
69+
};
70+
}
7471
}
7572

7673
export default new Component(COMPONENT_NAME, Metronome);
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<flat-button
2-
id="metronome-button"
2+
id="metronomeButton"
33
class="metronome-button"
44
click="onMetronomeClick"
55
ontext="Stop"
@@ -9,7 +9,7 @@
99

1010
<p class="metronome-label">Tempo:</p>
1111
<input
12-
id="metronome-input"
12+
id="metronomeInput"
1313
class="metronome-input"
1414
type="number"
1515
value="120" />

src/app/components/patch-midi-interface/index.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,28 @@ const COMPONENT_NAME = 'patch-midi-interface';
1313
const style = require(`./${COMPONENT_NAME}.css`);
1414
const markup = require(`./${COMPONENT_NAME}.html`);
1515

16-
const dom = [ 'deviceSelector', 'channelSelector', ];
16+
const dom = [ 'deviceSelector', 'channelSelector', 'noteInputContainer', 'noteInput' ];
1717

1818
class PatchMidiInterface extends BaseComponent {
1919
constructor(options) {
2020
super(style, markup, dom);
2121
this.eventModel = new PatchEventModel(this.schedule.bind(this));
2222
this.audioModel = new PatchAudioModel('MIDI Out', this.eventModel, PATCH_EVENT.MESSAGE, PATCH_EVENT.EMPTY);
2323
this.outputDevice;
24+
this.isNote = true;
25+
this.noteValue = 60;
2426
}
2527

2628
connectedCallback() {
2729
this.populateSelector();
30+
this.dom.noteInput.addEventListener('change', event => {
31+
const noteValue = parseInt(event.target.value, 10);
32+
if (isNaN(noteValue)) {
33+
this.dom.noteInput.value = 60;
34+
return;
35+
}
36+
this.noteValue = noteValue;
37+
});
2838
setTimeout(() => {
2939
const channels = IntArray(16).map(i => ({ label: i, value: i}));
3040
this.dom.channelSelector.setOptions(channels);
@@ -52,18 +62,33 @@ class PatchMidiInterface extends BaseComponent {
5262
});
5363
}
5464

55-
onChannelChange(channel) { this.channel = channel; }
65+
onChannelChange(channel) { this.channel = parseInt(channel, 10); }
66+
67+
onNoteCcToggle() {
68+
this.isNote = !this.isNote;
69+
this.isNote ?
70+
this.dom.noteInputContainer.classList.add('row-hidden') :
71+
this.dom.noteInputContainer.classList.remove('row-hidden');
72+
}
5673

57-
// TODO: note / cc toggle
5874
schedule(message) {
5975
if (!this.outputDevice) { return; }
76+
this.isNote ? this.scheduleNote(message) : this.scheduleCC(message);
77+
}
78+
79+
scheduleNote(message) {
6080
const offTime = message.time.midi + 100; // TODO: handle note duration
6181
const msgValue = 64;
6282
const onMessage = new MidiMessage(COMMAND.ON, this.channel, message.note, msgValue).serialize();
6383
const offMessage = new MidiMessage(COMMAND.OFF, this.channel, message.note, msgValue).serialize();
6484
this.outputDevice.send(onMessage, message.time.midi);
6585
this.outputDevice.send(offMessage, offTime);
6686
}
87+
88+
scheduleCC(message) {
89+
const ccMessage = new MidiMessage(COMMAND.CC, this.channel, this.noteValue, message.note).serialize();
90+
this.outputDevice.send(ccMessage, message.time.midi);
91+
}
6792
}
6893

6994
export default new Component(COMPONENT_NAME, PatchMidiInterface);
Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,46 @@
1-
.flex-end {
2-
justify-content: flex-end;
1+
.space-between {
2+
justify-content: space-between;
33
}
44

55
.row-margin {
66
margin-bottom: 4px;
77
}
88

99
.label {
10-
margin-right: 4px;
10+
margin-right: 8px;
11+
}
12+
13+
.note-toggle-row {
14+
justify-content: space-between;
15+
align-items: baseline;
16+
}
17+
18+
.note-row {
19+
justify-content: flex-end;
20+
align-items: baseline;
21+
width: 80px;
22+
transform: scaleX(1);
23+
opacity: 1;
24+
transition: 0.2s transform ease, 0.2s opacity ease;
25+
}
26+
27+
.row-hidden {
28+
transform-origin: right;
29+
transform: scaleX(0);
30+
opacity: 0;
31+
}
32+
33+
.note-input {
34+
height: 25px;
35+
font-size: 1em;
36+
margin: 0;
37+
width: 30px;
38+
background-color: var(--color-black-light);
39+
border: none;
40+
border-bottom: 1px solid var(--color-grey-light);
41+
outline: none;
42+
color: var(--color-grey-light);
43+
}
44+
.note-input:focus {
45+
outline: none;
1146
}

src/app/components/patch-midi-interface/patch-midi-interface.html

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div class="row flex-end row-margin">
1+
<div class="row space-between row-margin">
22
<flat-button
33
click="populateSelector"
44
ontext="&#8635;"
@@ -13,3 +13,20 @@
1313
<p class="label">Channel</p>
1414
<combo-box id="channelSelector" change="onChannelChange"></combo-box>
1515
</div>
16+
<div class="row note-toggle-row">
17+
<flat-button
18+
id="noteCcToggle"
19+
click="onNoteCcToggle"
20+
isToggle="true"
21+
ontext="CC"
22+
offtext="NOTE">
23+
</flat-button>
24+
<div id="noteInputContainer" class="row note-row row-hidden">
25+
<p class="label">Note</p>
26+
<input
27+
id="noteInput"
28+
class="note-input"
29+
type="number"
30+
value="60" />
31+
</div>
32+
</div>

0 commit comments

Comments
 (0)