UNPKG

16.1 kBJavaScriptView Raw
1(async () => {
2 const isCI = ["8081", "8082"].includes(location.port) && location.search.includes("ci=true");
3 const url = "/dist/csound.js"; // isCI ? "/csound.esm.js" : "/csound.dev.esm.js";
4 const { Csound } = await import(url);
5
6 const helloWorld = `
7<CsoundSynthesizer>
8<CsOptions>
9 -odac
10</CsOptions>
11<CsInstruments>
12 instr 1
13 prints "Hello World!\\n"
14 endin
15</CsInstruments>
16<CsScore>
17 i 1 0 0
18</CsScore>
19</CsoundSynthesizer>
20`;
21
22 const shortTone = `
23<CsoundSynthesizer>
24<CsOptions>
25 -odac
26</CsOptions>
27<CsInstruments>
28
29 chnset(1, "test1")
30 chnset(2, "test2")
31
32 instr 1
33 out poscil(0dbfs/3, 440) * linen:a(1, .01, p3, .01)
34 endin
35</CsInstruments>
36<CsScore>
37 i 1 0 2
38</CsScore>
39</CsoundSynthesizer>
40`;
41
42 const shortTone2 = `
43<CsoundSynthesizer>
44<CsOptions>
45 -odac
46</CsOptions>
47<CsInstruments>
48 0dbfs = 1
49
50 chnset(440, "freq")
51
52 instr 1
53 out poscil(0dbfs/3, chnget:k("freq")) * linen:a(1, .01, p3, .01)
54 endin
55</CsInstruments>
56<CsScore>
57 i 1 0 1
58</CsScore>
59</CsoundSynthesizer>
60`;
61
62 const stringChannelTest = `
63<CsoundSynthesizer>
64<CsOptions>
65 -odac
66</CsOptions>
67<CsInstruments>
68 0dbfs = 1
69
70 instr 1
71 chnset("test0", "strChannel")
72 turnoff
73 endin
74
75</CsInstruments>
76<CsScore>
77 i 1 0 2
78 e 2 0
79</CsScore>
80</CsoundSynthesizer>
81`;
82
83 const pluginTest = `
84<CsoundSynthesizer>
85<CsOptions>
86 -odac
87</CsOptions>
88<CsInstruments>
89 0dbfs=1
90 instr 1
91 i1 = 2
92 i2 = 2
93 i3 mult i1, i2
94 print i3
95 endin
96 instr 2
97 k1 = 2
98 k2 = 2
99 k3 mult k1, k2
100 printk2 k3
101 endin
102 instr 3
103 a1 oscili 0dbfs, 440
104 a2 oscili 0dbfs, 356
105 a3 mult a1, a2
106 out a3
107 endin
108</CsInstruments>
109<CsScore>
110 i1 0 0
111 i2 0 1
112 i3 0 2
113 e 0 0
114</CsScore>
115</CsoundSynthesizer>
116`;
117
118 const cxxPluginTest = `
119<CsoundSynthesizer>
120<CsOptions>
121 -odac
122</CsOptions>
123<CsInstruments>
124 0dbfs=1
125instr 1
126 kcone_lengths[] fillarray 0.0316, 0.051, .3, 0.2
127 kradii_in[] fillarray 0.0055, 0.00635, 0.0075, 0.0075
128 kradii_out[] fillarray 0.0055, 0.0075, 0.0075, 0.0275
129 kcurve_type[] fillarray 1, 1, 1, 2
130 kLength linseg 0.2, 2, 0.3
131 kPick_Pos = 1.0
132 kEndReflection init 1.0
133 kEndReflection = 1.0
134 kDensity = 1.0
135 kComputeVisco = 0
136 aImpulse mpulse .5, .1
137 aFeedback, aSound resontube 0.005*aImpulse, kLength, kcone_lengths, kradii_in, kradii_out, kcurve_type, kEndReflection, kDensity, kPick_Pos, kComputeVisco
138 out aSound
139endin
140</CsInstruments>
141<CsScore>
142i1 0 2
143</CsScore>
144</CsoundSynthesizer>
145`;
146
147 const ftableTest = `
148<CsoundSynthesizer>
149<CsOptions>
150 -odac
151</CsOptions>
152<CsInstruments>
153 instr 1
154 prints "Hello Fibonnaci!\\n"
155 prints "Table length %d\\n", tableng:i(1)
156 endin
157</CsInstruments>
158<CsScore>
159 f 1 0 8 -2 0 1 1 2 3 5 8 13
160 i 1 0 -1
161</CsScore>
162</CsoundSynthesizer>
163`;
164
165 const samplesTest = `
166<CsoundSynthesizer>
167<CsOptions>
168-odac
169</CsOptions>
170<CsInstruments>
171sr = 44100
172ksmps = 32
173nchnls = 1
1740dbfs = 1
175
176instr 1
177 Ssample = "tiny_test_sample.wav"
178 aRead[] diskin Ssample, 1, 0, 0
179 out aRead[0], aRead[0]
180endin
181
182instr 2
183 aSig monitor
184 fout "monitor_out.wav", 4, aSig
185endin
186
187</CsInstruments>
188<CsScore>
189i 2 0 0.3
190i 1 0 0.1
191i 1 + .
192i 1 + .
193e
194</CsScore>
195</CsoundSynthesizer>
196`;
197
198 mocha.setup({ ui: "bdd", timeout: 10000 }).fullTrace();
199
200 if (isCI) {
201 MochaWebdriverClient.install(mocha);
202 }
203
204 const csoundVariations = [
205 { useWorker: false, useSPN: false, name: "SINGLE THREAD, AW" },
206 { useWorker: false, useSPN: true, name: "SINGLE THREAD, SPN" },
207 { useWorker: true, useSAB: true, name: "WORKER, AW, SAB" },
208 { useWorker: true, useSAB: false, name: "WORKER, AW, Messageport" },
209 { useWorker: true, useSAB: false, useSPN: true, name: "WORKER, SPN, MessagePort" },
210 ];
211
212 csoundVariations.forEach((test) => {
213 describe(`@csound/browser : ${test.name}`, async function () {
214 this.timeout(10000);
215 it("can be started", async function () {
216 const cs = await Csound(test);
217 console.log(`Csound version: ${cs.name}`);
218 const startReturn = await cs.start();
219 assert.equal(startReturn, 0);
220 await cs.stop();
221 cs.terminateInstance && (await cs.terminateInstance());
222 });
223
224 it("has expected methods", async function () {
225 const cs = await Csound(test);
226 assert.property(cs, "getAudioContext", "has .getAudioContext() method");
227 assert.property(cs, "start", "has .start() method");
228 assert.property(cs, "stop", "has .stop() method");
229 assert.property(cs, "pause", "has .pause() method");
230 await cs.stop();
231 await cs.terminateInstance();
232 });
233
234 it("can use run using just compileOrc", async function () {
235 const cs = await Csound(test);
236 await cs.compileOrc(`
237 ksmps=64
238 instr 1
239 out oscili(.25, 110)
240 endin
241 schedule(1,0,1)
242 `);
243 const startReturn = await cs.start();
244 assert.equal(startReturn, 0);
245 await cs.stop();
246 await cs.terminateInstance();
247 });
248
249 it("can play tone and get channel values", async function () {
250 const cs = await Csound(test);
251 const compileReturn = await cs.compileCsdText(shortTone);
252 assert.equal(compileReturn, 0);
253 const startReturn = await cs.start();
254
255 assert.equal(startReturn, 0);
256 assert.equal(1, await cs.getControlChannel("test1"));
257 assert.equal(2, await cs.getControlChannel("test2"));
258 await cs.stop();
259 await cs.terminateInstance();
260 });
261
262 it("can play tone and send channel values", async function () {
263 const cs = await Csound(test);
264 const compileReturn = await cs.compileCsdText(shortTone2);
265 assert.equal(compileReturn, 0);
266 const startReturn = await cs.start();
267 assert.equal(startReturn, 0);
268 await cs.setControlChannel("freq", 880);
269 assert.equal(880, await cs.getControlChannel("freq"));
270 await cs.stop();
271 await cs.terminateInstance();
272 });
273
274 it("can send and receive string channel values", async function () {
275 const cs = await Csound(test);
276 const compileReturn = await cs.compileCsdText(stringChannelTest);
277 assert.equal(compileReturn, 0);
278 const startReturn = await cs.start();
279 assert.equal(startReturn, 0);
280 assert.equal("test0", await cs.getStringChannel("strChannel"));
281 await cs.setStringChannel("strChannel", "test1");
282 assert.equal("test1", await cs.getStringChannel("strChannel"));
283 await cs.stop();
284 await cs.terminateInstance();
285 });
286
287 it("can load and run plugins", async function () {
288 const testWithPlugin = Object.assign(
289 {
290 withPlugins: ["./plugin_example.wasm"],
291 },
292 test,
293 );
294 const cs = await Csound(testWithPlugin);
295 assert.equal(0, await cs.compileCsdText(pluginTest));
296 await cs.start();
297 await cs.stop();
298 await cs.terminateInstance();
299 });
300
301 it("can load and run c++ plugins", async function () {
302 const testWithPlugin = Object.assign(
303 {
304 withPlugins: ["./plugin_example_cxx.wasm"],
305 },
306 test,
307 );
308 const cs = await Csound(testWithPlugin);
309
310 assert.equal(0, await cs.compileCsdText(cxxPluginTest));
311 await cs.start();
312 await cs.stop();
313 await cs.terminateInstance();
314 });
315
316 it("emits public events in realtime performance", async function () {
317 if (test.name !== "WORKER, AW, SAB") {
318 const eventPlaySpy = sinon.spy();
319 const eventPauseSpy = sinon.spy();
320 const eventStopSpy = sinon.spy();
321 const eventOnAudioNodeCreatedSpy = sinon.spy();
322
323 const csoundObj = await Csound(test);
324
325 csoundObj.on("play", eventPlaySpy);
326 csoundObj.on("pause", eventPauseSpy);
327 csoundObj.on("stop", eventStopSpy);
328 csoundObj.on("onAudioNodeCreated", eventOnAudioNodeCreatedSpy);
329
330 await csoundObj.setOption("-odac");
331 await csoundObj.compileCsdText(shortTone);
332 await csoundObj.start();
333 await csoundObj.pause();
334 await csoundObj.resume();
335 await csoundObj.stop();
336
337 assert(eventPlaySpy.calledTwice, 'The "play" event was emitted twice');
338 assert(eventPauseSpy.calledOnce, 'The "pause" event was emitted once');
339 assert(eventStopSpy.calledOnce, 'The "stop" event was emitted once');
340 assert(
341 eventOnAudioNodeCreatedSpy.calledOnce,
342 'The "onAudioNodeCreated" event was emitted once',
343 );
344 assert(
345 eventOnAudioNodeCreatedSpy.calledWith(sinon.match.instanceOf(AudioNode)),
346 'The argument provided to the callback of "onAudioNodeCreated" was an AudioNode',
347 );
348 await csoundObj.terminateInstance();
349 }
350 });
351
352 it("can read and write ftables in realtime", async function () {
353 const csoundObj = await Csound(test);
354 await csoundObj.setOption("-odac");
355 await csoundObj.compileCsdText(ftableTest);
356 await csoundObj.start();
357
358 // assert few indicies
359 assert.equal(8, await csoundObj.tableLength(1), "The length of the table counts as 8");
360 assert.equal(0, await csoundObj.tableGet(1, 0, "The first index is 0"));
361 assert.equal(1, await csoundObj.tableGet(1, 1, "The second index is 1"));
362 assert.equal(1, await csoundObj.tableGet(1, 2, "The third index is 2"));
363 assert.equal(2, await csoundObj.tableGet(1, 3, "The fourth index is 3"));
364
365 await csoundObj.tableSet(1, 0, 123);
366 await csoundObj.tableSet(1, 1, 666);
367
368 assert.equal(123, await csoundObj.tableGet(1, 0, "The first index was modified to 123"));
369 assert.equal(666, await csoundObj.tableGet(1, 1, "The second index was modified to 666"));
370
371 await csoundObj.stop();
372 await csoundObj.terminateInstance();
373 });
374
375 it("can read and write arraybuffers to/from ftables in realtime", async function () {
376 const csoundObj = await Csound(test);
377 await csoundObj.setOption("-odac");
378 await csoundObj.compileCsdText(ftableTest);
379 await csoundObj.start();
380
381 const tableLength = await csoundObj.tableLength(1);
382
383 // we initialize a float64 typed array
384 // using the length of the original csound table
385 const float64array = new Float64Array(tableLength);
386
387 // we then fill the arrays with test values
388 float64array.set([1, 1.1, 1.01, 1.001]);
389
390 // then we copy the the array from js into csound's runtime onto table 1
391 await csoundObj.tableCopyIn(1, float64array);
392
393 // assert that the values got delivered
394 assert.equal(
395 float64array[0],
396 await csoundObj.tableGet(1, 0),
397 "The first index from table1 matches the first index of the copied array",
398 );
399 assert.equal(
400 float64array[1],
401 await csoundObj.tableGet(1, 1),
402 "The second index from table1 matches the second index of the copied array",
403 );
404 assert.equal(
405 float64array[2],
406 await csoundObj.tableGet(1, 2),
407 "The third index from table1 matches the third index of the copied array",
408 );
409 assert.equal(
410 float64array[3],
411 await csoundObj.tableGet(1, 3),
412 "The fourth index from table1 matches the fourth index of the copied array",
413 );
414
415 const csoundTableOneFloat64 = await csoundObj.tableCopyOut(1);
416 // we convert it to normal Array for readability
417 const csoundTableOneArray = Array.from(csoundTableOneFloat64);
418 assert.deepEqual(
419 csoundTableOneArray,
420 [1, 1.1, 1.01, 1.001, 0, 0, 0, 0],
421 "The current csound table matches the 4 numbers we copied into it followed by 4 empty values (0)",
422 );
423 await csoundObj.stop();
424 await csoundObj.terminateInstance();
425 });
426
427 it("can stop() and reset() without start()", async function () {
428 const csoundObj = await Csound(test);
429 await csoundObj.stop();
430 await csoundObj.reset();
431 await csoundObj.start();
432 await csoundObj.stop();
433 await csoundObj.terminateInstance();
434 });
435
436 it("can start() -> stop() -> reset() and start again", async function () {
437 const csoundObj = await Csound(test);
438 await csoundObj.compileCsdText(helloWorld);
439 await csoundObj.start();
440 await csoundObj.stop();
441 await csoundObj.reset();
442 await csoundObj.compileCsdText(helloWorld);
443 await csoundObj.start();
444 await csoundObj.stop();
445 await csoundObj.terminateInstance();
446 });
447
448 it("can play a sample, write a sample and read the output file", async function () {
449 const csoundObj = await Csound(test);
450 const response = await fetch("tiny_test_sample.wav");
451 const testSampleArrayBuffer = await response.arrayBuffer();
452 const testSample = new Uint8Array(testSampleArrayBuffer);
453 await csoundObj.fs.writeFile("tiny_test_sample.wav", testSample);
454
455 // allow the example to play until the end
456 let endResolver;
457 const waitUntilEnd = new Promise((resolve) => {
458 endResolver = resolve;
459 });
460 csoundObj.on("realtimePerformanceEnded", endResolver);
461
462 assert.include(
463 await csoundObj.fs.readdir("/"),
464 "tiny_test_sample.wav",
465 "The sample was written into the root dir",
466 );
467
468 assert.equal(0, await csoundObj.compileCsdText(samplesTest), "The test string is valid");
469 assert.equal(
470 0,
471 await csoundObj.start(),
472 "Csounds starts normally, indicating the sample was found",
473 );
474
475 await waitUntilEnd;
476 assert.include(
477 await csoundObj.fs.readdir("/"),
478 "monitor_out.wav",
479 "The sample which csound wrote with fout, is accessible after the end of performance",
480 );
481 await csoundObj.terminateInstance();
482 });
483
484 it("can play a csd from a nested filesystem directory, with code requiring a sample", async function () {
485 const csoundObj = await Csound(test);
486 const response = await fetch("/tiny_test_sample.wav");
487 const testSampleArrayBuffer = await response.arrayBuffer();
488 const testSample = new Uint8Array(testSampleArrayBuffer);
489
490 // Writing the csd to disk
491 const csdPath = "/anycsd.csd";
492 await csoundObj.fs.mkdir("/somedir");
493 await csoundObj.fs.writeFile("tiny_test_sample.wav", testSample);
494 await csoundObj.fs.writeFile(csdPath, samplesTest);
495
496 // allow the example to play until the end
497 let endResolver;
498 const waitUntilEnd = new Promise((resolve) => {
499 endResolver = resolve;
500 });
501 csoundObj.on("realtimePerformanceEnded", endResolver);
502
503 assert.include(
504 await csoundObj.fs.readdir("/"),
505 "tiny_test_sample.wav",
506 "The sample was written into the root dir",
507 );
508
509 assert.equal(0, await csoundObj.compileCsd(csdPath), "The test Csd is valid");
510 assert.equal(
511 0,
512 await csoundObj.start(),
513 "Csounds starts normally, indicating the sample was found",
514 );
515
516 await waitUntilEnd;
517 assert.include(
518 await csoundObj.fs.readdir("/"),
519 "monitor_out.wav",
520 "The sample which csound wrote with fout, is accessible after the end of performance",
521 );
522 await csoundObj.terminateInstance();
523 });
524 });
525 });
526
527 const triggerEvent = "ontouchstart" in document.documentElement ? "touchend" : "click";
528 document.querySelector("#all_tests").addEventListener(triggerEvent, async function () {
529 mocha.fullTrace(true);
530 mocha.checkLeaks(false); // worker + spn defenitely leaks
531 mocha.cleanReferencesAfterRun(true);
532 mocha.run();
533 });
534 if (isCI) {
535 mocha.cleanReferencesAfterRun(true);
536 mocha.run();
537 }
538})();