UNPKG

7.2 kBJavaScriptView Raw
1/* global Uint8Array Promise */
2const tap = require('tap');
3const {AudioContext} = require('web-audio-test-api');
4
5const AudioEngine = require('../src/AudioEngine');
6
7
8tap.test('SoundPlayer', suite => {
9
10 let audioContext;
11 let audioEngine;
12 let soundPlayer;
13
14 const help = {
15 get engineInputs () {
16 return audioEngine.inputNode.toJSON().inputs;
17 }
18 };
19
20 suite.beforeEach(() => {
21 audioContext = new AudioContext();
22 audioEngine = new AudioEngine(audioContext);
23 // sound will be 0.2 seconds long
24 audioContext.DECODE_AUDIO_DATA_RESULT = audioContext.createBuffer(2, 8820, 44100);
25 audioContext.DECODE_AUDIO_DATA_FAILED = false;
26 const data = new Uint8Array(0);
27 return audioEngine.decodeSoundPlayer({data}).then(result => {
28 soundPlayer = result;
29 });
30 });
31
32 suite.afterEach(() => {
33 soundPlayer.dispose();
34 soundPlayer = null;
35 audioEngine = null;
36 audioContext.$reset();
37 audioContext = null;
38 });
39
40 suite.plan(5);
41
42 suite.test('play initializes and creates source node', t => {
43 t.plan(3);
44 t.equal(soundPlayer.initialized, false, 'not yet initialized');
45 soundPlayer.play();
46 t.equal(soundPlayer.initialized, true, 'now is initialized');
47 t.deepEqual(soundPlayer.outputNode.toJSON(), {
48 buffer: audioContext.DECODE_AUDIO_DATA_RESULT.toJSON(),
49 inputs: [],
50 loop: false,
51 loopEnd: 0,
52 loopStart: 0,
53 name: 'AudioBufferSourceNode',
54 playbackRate: {
55 inputs: [],
56 value: 1
57 }
58 });
59
60 t.end();
61 });
62
63 suite.test('connect', t => {
64 t.plan(1);
65 soundPlayer.play();
66 soundPlayer.connect(audioEngine);
67 t.deepEqual(help.engineInputs, [
68 soundPlayer.outputNode.toJSON()
69 ], 'output node connects to input node');
70 t.end();
71 });
72
73 suite.test('stop decay', t => {
74 t.plan(5);
75 soundPlayer.play();
76 soundPlayer.connect(audioEngine);
77 const outputNode = soundPlayer.outputNode;
78
79 audioContext.$processTo(0);
80 soundPlayer.stop();
81 t.equal(soundPlayer.outputNode, null, 'nullify outputNode immediately (taken sound is stopping)');
82 t.deepEqual(help.engineInputs, [{
83 name: 'GainNode',
84 gain: {
85 value: 1,
86 inputs: []
87 },
88 inputs: [outputNode.toJSON()]
89 }], 'output node connects to gain node to input node');
90
91 audioContext.$processTo(audioEngine.DECAY_DURATION / 2);
92 t.equal(outputNode.$state, 'PLAYING');
93
94 audioContext.$processTo(audioEngine.DECAY_DURATION + 0.001);
95 t.deepEqual(help.engineInputs, [{
96 name: 'GainNode',
97 gain: {
98 value: 0,
99 inputs: []
100 },
101 inputs: [outputNode.toJSON()]
102 }], 'output node connects to gain node to input node decayed');
103
104 t.equal(outputNode.$state, 'FINISHED');
105
106 t.end();
107 });
108
109 suite.test('play while playing debounces', t => {
110 t.plan(7);
111 const log = [];
112 soundPlayer.connect(audioEngine);
113 soundPlayer.play();
114 t.equal(soundPlayer.isStarting, true, 'player.isStarting');
115 const originalNode = soundPlayer.outputNode;
116 // the second play should still "finish" this play
117 soundPlayer.finished().then(() => log.push('finished first'));
118 soundPlayer.play();
119 soundPlayer.finished().then(() => log.push('finished second'));
120 soundPlayer.play();
121 soundPlayer.finished().then(() => log.push('finished third'));
122 soundPlayer.play();
123 t.equal(originalNode, soundPlayer.outputNode, 'same output node');
124 t.equal(soundPlayer.outputNode.$state, 'PLAYING');
125 return Promise.resolve().then(() => {
126 t.deepEqual(log, ['finished first', 'finished second', 'finished third'], 'finished in order');
127
128 // fast forward to one ms before decay time
129 audioContext.$processTo(audioEngine.DECAY_DURATION - 0.001);
130 soundPlayer.play();
131
132 t.equal(originalNode, soundPlayer.outputNode, 'same output node');
133
134
135 // now at DECAY_DURATION, we should meet a new player as the old one is taken/stopped
136 audioContext.$processTo(audioEngine.DECAY_DURATION);
137
138 t.equal(soundPlayer.isStarting, false, 'player.isStarting now false');
139
140 soundPlayer.play();
141 t.notEqual(originalNode, soundPlayer.outputNode, 'New output node');
142
143 t.end();
144 });
145
146 });
147
148 suite.test('play while playing', t => {
149 t.plan(15);
150 const log = [];
151 soundPlayer.play();
152 soundPlayer.finished().then(() => log.push('play 1 finished'));
153 soundPlayer.connect(audioEngine);
154 const firstPlayNode = soundPlayer.outputNode;
155
156 // go past debounce time and play again
157 audioContext.$processTo(audioEngine.DECAY_DURATION);
158
159 return Promise.resolve()
160 .then(() => {
161
162 t.equal(soundPlayer.outputNode.$state, 'PLAYING');
163
164 soundPlayer.play();
165 soundPlayer.finished().then(() => log.push('play 2 finished'));
166
167 // wait for a micro-task loop to fire our previous events
168 return Promise.resolve();
169 })
170 .then(() => {
171
172 t.equal(log[0], 'play 1 finished');
173 t.notEqual(soundPlayer.outputNode, firstPlayNode, 'created new player node');
174
175 t.equal(help.engineInputs.length, 2, 'there should be 2 players connected');
176 t.equal(firstPlayNode.$state, 'PLAYING');
177 t.equal(soundPlayer.outputNode.$state, 'PLAYING');
178 t.equal(help.engineInputs[0].gain.value, 1, 'old sound connectect to gain node with volume 1');
179
180 const {currentTime} = audioContext;
181 audioContext.$processTo(currentTime + audioEngine.DECAY_WAIT + 0.001);
182 t.notEqual(help.engineInputs[0].gain.value, 1,
183 'old sound connected to gain node which will fade');
184
185 audioContext.$processTo(currentTime + audioEngine.DECAY_WAIT + audioEngine.DECAY_DURATION + 0.001);
186 t.equal(soundPlayer.outputNode.$state, 'PLAYING');
187 t.equal(firstPlayNode.$state, 'FINISHED');
188
189 t.equal(help.engineInputs[0].gain.value, 0, 'faded old sound to 0');
190
191 t.equal(log.length, 1);
192 audioContext.$processTo(currentTime + audioEngine.DECAY_WAIT + audioEngine.DECAY_DURATION + 0.3);
193
194 // wait for a micro-task loop to fire our previous events
195 return Promise.resolve();
196 })
197 .then(() => {
198
199 t.equal(log[1], 'play 2 finished');
200 t.equal(help.engineInputs.length, 1, 'old sound disconneted itself after done');
201 t.equal(log.length, 2);
202
203 t.end();
204 });
205 });
206
207 suite.end();
208});