-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmusic.js
118 lines (74 loc) · 2.33 KB
/
music.js
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
function Music() {
var self = this,
vol;
self.context = new AudioContext();
vol = self.context.createGain();
vol.gain.value = 0.05; // Sound is very loud by default, that's strange.
vol.connect(self.context.destination);
self.dest = vol; // Gain node is the destination for track oscillators.
self.tracks = [];
self.timers = new Timers();
self.start(MUSIC);
window.addEventListener('keydown', function (e) {
if (e.code === 'KeyQ') {
if (self.isPlaying) {
self.end();
} else {
self.start(MUSIC);
}
}
});
}
Music.prototype.start = function (composition) {
var self = this;
self.tracks = [];
Object.keys(composition).forEach(function (trackName) {
self.tracks.push(new Track(self.context, self.dest, composition[trackName], self.timers));
});
self.tracks.forEach(function (track) {
track.play();
});
self.isPlaying = true;
};
Music.prototype.end = function () {
var self = this;
self.tracks.forEach(function (track) {
track.stop();
});
self.isPlaying = false;
};
function Track(ctx, dest, track, timers) {
var self = this;
self.ctx = ctx;
self.dest = dest;
self.track = track;
self.pos = 0;
self.timers = timers;
}
Track.prototype.play = function () {
var self = this,
notes = self.track.notes,
note,
noteValue;
if (self.pos == notes.length) self.pos = 0;
note = notes[self.pos];
noteValue = WHOLE_NOTE / notes[self.pos + 1];
// For note == 0 is a pause.
note && self.playNote(notes[self.pos], noteValue);
self.pos += 2;
self.timers.setTimeout(self.play.bind(self), noteValue);
};
Track.prototype.playNote = function (note, noteValue) {
var self = this,
osc = self.ctx.createOscillator();
osc.frequency.value = NOTES[note] / 2;
osc.type = self.track.oType || 'sine'; // Options: sine, square, sawtooth, triangle.
osc.connect(self.dest);
osc.start();
// Here, `triggerBeforeClear` is `true`. It guarantees the immediate stop of the sound.
self.timers.setTimeout(osc.stop.bind(osc), noteValue - 20, true);
};
Track.prototype.stop = function () {
// Stop all the oscillators on all tracks.
this.timers.nullifyTimers();
};