UNPKG

3.54 kBJavaScriptView Raw
1import { FADEIN, FADEOUT, createFadeIn, createFadeOut } from "fade-maker";
2export default class {
3 constructor(ac, buffer) {
4 this.ac = ac;
5 this.gain = 1;
6 this.buffer = buffer;
7 this.destination = this.ac.destination;
8 this.ac.createStereoPanner = ac.createStereoPanner || ac.createPanner;
9 }
10
11 applyFade(type, start, duration, shape = "logarithmic") {
12 if (type === FADEIN) {
13 createFadeIn(this.fadeGain.gain, shape, start, duration);
14 } else if (type === FADEOUT) {
15 createFadeOut(this.fadeGain.gain, shape, start, duration);
16 } else {
17 throw new Error("Unsupported fade type");
18 }
19 }
20
21 applyFadeIn(start, duration, shape = "logarithmic") {
22 this.applyFade(FADEIN, start, duration, shape);
23 }
24
25 applyFadeOut(start, duration, shape = "logarithmic") {
26 this.applyFade(FADEOUT, start, duration, shape);
27 }
28
29 isPlaying() {
30 return this.source !== undefined;
31 }
32
33 getDuration() {
34 return this.buffer.duration;
35 }
36
37 setAudioContext(ac) {
38 this.ac = ac;
39 this.ac.createStereoPanner = ac.createStereoPanner || ac.createPanner;
40 this.destination = this.ac.destination;
41 }
42
43 setUpSource() {
44 this.source = this.ac.createBufferSource();
45 this.source.buffer = this.buffer;
46 const sourcePromise = new Promise(resolve => {
47 // keep track of the buffer state.
48 this.source.onended = () => {
49 this.source.disconnect();
50 this.fadeGain.disconnect();
51 this.volumeGain.disconnect();
52 this.shouldPlayGain.disconnect();
53 this.panner.disconnect();
54 this.masterGain.disconnect();
55 this.source = undefined;
56 this.fadeGain = undefined;
57 this.volumeGain = undefined;
58 this.shouldPlayGain = undefined;
59 this.panner = undefined;
60 this.masterGain = undefined;
61 resolve();
62 };
63 });
64 this.fadeGain = this.ac.createGain(); // used for track volume slider
65
66 this.volumeGain = this.ac.createGain(); // used for solo/mute
67
68 this.shouldPlayGain = this.ac.createGain();
69 this.masterGain = this.ac.createGain();
70 this.panner = this.ac.createStereoPanner();
71 this.source.connect(this.fadeGain);
72 this.fadeGain.connect(this.volumeGain);
73 this.volumeGain.connect(this.shouldPlayGain);
74 this.shouldPlayGain.connect(this.masterGain);
75 this.masterGain.connect(this.panner);
76 this.panner.connect(this.destination);
77 return sourcePromise;
78 }
79
80 setVolumeGainLevel(level) {
81 if (this.volumeGain) {
82 this.volumeGain.gain.value = level;
83 }
84 }
85
86 setShouldPlay(bool) {
87 if (this.shouldPlayGain) {
88 this.shouldPlayGain.gain.value = bool ? 1 : 0;
89 }
90 }
91
92 setMasterGainLevel(level) {
93 if (this.masterGain) {
94 this.masterGain.gain.value = level;
95 }
96 }
97
98 setStereoPanValue(value) {
99 const pan = value === undefined ? 0 : value;
100
101 if (this.panner) {
102 if (this.panner.pan !== undefined) {
103 this.panner.pan.value = pan;
104 } else {
105 this.panner.panningModel = "equalpower";
106 this.panner.setPosition(pan, 0, 1 - Math.abs(pan));
107 }
108 }
109 }
110 /*
111 source.start is picky when passing the end time.
112 If rounding error causes a number to make the source think
113 it is playing slightly more samples than it has it won't play at all.
114 Unfortunately it doesn't seem to work if you just give it a start time.
115 */
116
117
118 play(when, start, duration) {
119 this.source.start(when, start, duration);
120 }
121
122 stop(when = 0) {
123 if (this.source) {
124 this.source.stop(when);
125 }
126 }
127
128}
\No newline at end of file