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
|
import Trombone from './vend/trombone.mjs';
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.audioCtx = new AudioContext();
this.wave = new PeriodicWave(this.audioCtx, {
real: Trombone.real,
imag: Trombone.imag
});
this.globalGain = this.audioCtx.createGain();
this.globalGain.connect(this.audioCtx.destination);
this.globalGain.gain.setValueAtTime(this.volume, this.audioCtx.currentTime);
this.querySelector('.volume').onchange = e => {
console.debug('vol changed', e, this.volume);
this.globalGain.gain.setValueAtTime(this.volume, this.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() {
console.debug('Player#isPlaying', this, this.classList.contains('playing'));
return this.classList.contains('playing');
}
// offsets is an array of offsets in cents from A4.
get offsets() {
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();
}
}
update() {
console.debug('Player#update', this.offsets, this.notes);
for (let i = 0; i < this.offsets.length; i++) {
if (!this.notes[i]) {
console.debug('new note for', i)
const note = new OscillatorNode(this.audioCtx, {
frequency: this.aFreq,
type: 'custom',
periodicWave: this.wave
});
note.connect(this.globalGain);
note.start();
this.notes[i] = note;
}
const note = this.notes[i];
note.detune.setValueAtTime(this.offsets[i], this.audioCtx.currentTime);
}
for (let j = this.notes.length-1; j >= this.offsets.length; j--) {
const note = this.notes[j];
note.stop();
}
this.notes = this.notes.slice(0, this.offsets.length);
console.debug(' -- done', this.notes);
}
start() {
console.debug('Player#start', this.notes);
this.update();
this.audioCtx.resume();
}
stop() {
console.debug('Player#stop', this);
this.audioCtx.suspend();
}
}
|