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
|
import { Note, toCents } from './scale.mjs';
import GuitarFuzz from './vend/guitar-fuzz.mjs';
import Trombone from './vend/trombone.mjs';
const audioCtx = new AudioContext();
const fuzzWave = new PeriodicWave(audioCtx, {
real: GuitarFuzz.real,
imag: GuitarFuzz.imag
});
const tromboneWave = new PeriodicWave(audioCtx, {
real: Trombone.real,
imag: Trombone.imag
});
export default class extends HTMLElement {
static name = 'x-player'
static register() {
console.debug('Registering Element', this.name, this);
customElements.define(this.name, this);
}
// A4
aFreq = 440;
// oscillatornodes
notes = [];
getOffsets = () => [];
constructor() {
super();
console.debug('Player#constructor', this);
}
connectedCallback() {
this.globalGain = audioCtx.createGain();
this.globalGain.connect(audioCtx.destination);
this.globalGain.gain.setValueAtTime(this.volume, audioCtx.currentTime);
this.querySelector('.volume').onchange = e => {
console.debug('vol changed', e, this.volume);
this.globalGain.gain.setValueAtTime(this.volume, audioCtx.currentTime);
}
}
get volume() {
console.debug('Player#volume', this.querySelector('.volume').value)
return Number(this.querySelector('.volume').value);
}
set volume(volume) {
console.debug('Player#volume()', this, volume);
this.querySelector('.volume').value = volume;
}
get isPlaying() {
return this.classList.contains('playing');
}
// offsets is an array of offsets in cents from A4.
get offsets() {
console.debug('offsets getoffsets', this.getOffsets);
return this.getOffsets();
}
set isPlaying(isPlaying) {
console.debug('Player#isPlaying', this, isPlaying);
if (isPlaying) {
this.classList.add('playing');
this.start();
} else {
this.classList.remove('playing');
this.stop();
}
}
start() {
console.debug('Player#start', this);
for (let i = 0; i < this.offsets.length; i++) {
if (!this.notes[i]) {
const note = new OscillatorNode(audioCtx, {
frequency: this.aFreq,
type: 'custom',
periodicWave: tromboneWave,
});
note.connect(this.globalGain);
note.start();
this.notes[i] = note;
}
const note = this.notes[i];
note.detune.setValueAtTime(this.offsets[i], audioCtx.currentTime);
}
for (let j = this.notes.length-1; j >= this.offsets.length; j--) {
this.notes[j].stop();
delete this.notes[j];
}
}
stop() {
console.debug('Player#stop', this);
this.notes.forEach(n => n.stop());
this.notes = [];
}
}
|