Skip to content

Commit da24653

Browse files
Fix: Resolve p5 is not defined ReferenceError by adding p5-adapter (sugarlabs#4990)
* Fix: Resolve p5/Tone.js conflict using adapter pattern * style: apply prettier formatting to adapter files
1 parent 633943c commit da24653

File tree

3 files changed

+120
-14
lines changed

3 files changed

+120
-14
lines changed

js/loader.js

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,45 @@
1414
requirejs.config({
1515
baseUrl: "lib",
1616
shim: {
17-
easel: {
17+
"easel": {
1818
exports: "createjs"
19+
},
20+
"p5.min": {
21+
exports: "p5"
22+
},
23+
"p5-adapter": {
24+
deps: ["p5.min"]
25+
},
26+
"p5.sound.min": {
27+
deps: ["p5-adapter"]
28+
},
29+
"p5.dom.min": {
30+
deps: ["p5.min"]
31+
},
32+
"p5-sound-adapter": {
33+
deps: ["p5.sound.min"]
1934
}
2035
},
2136
paths: {
22-
utils: "../js/utils",
23-
widgets: "../js/widgets",
24-
activity: "../js",
25-
easel: "../lib/easeljs",
26-
twewn: "../lib/tweenjs",
27-
prefixfree: "../bower_components/prefixfree/prefixfree.min",
28-
samples: "../sounds/samples",
29-
planet: "../js/planet",
30-
tonejsMidi: "../node_modules/@tonejs/midi/dist/Midi",
31-
i18next: [
37+
"utils": "../js/utils",
38+
"widgets": "../js/widgets",
39+
"activity": "../js",
40+
"easel": "../lib/easeljs",
41+
"twewn": "../lib/tweenjs",
42+
"prefixfree": "../bower_components/prefixfree/prefixfree.min",
43+
"samples": "../sounds/samples",
44+
"planet": "../js/planet",
45+
"tonejsMidi": "../node_modules/@tonejs/midi/dist/Midi",
46+
"p5.min": "../lib/p5.min",
47+
"p5.sound.min": "../lib/p5.sound.min",
48+
"p5.dom.min": "../lib/p5.dom.min",
49+
"p5-adapter": "../js/p5-adapter",
50+
"p5-sound-adapter": "../js/p5-sound-adapter",
51+
"i18next": [
3252
"../lib/i18next.min",
3353
"https://cdn.jsdelivr.net/npm/i18next@23.11.5/dist/umd/i18next.min"
3454
],
35-
i18nextHttpBackend: [
55+
"i18nextHttpBackend": [
3656
"../lib/i18nextHttpBackend.min",
3757
"https://cdn.jsdelivr.net/npm/i18next-http-backend@2.5.1/i18nextHttpBackend.min"
3858
]
@@ -69,7 +89,7 @@ requirejs(["i18next", "i18nextHttpBackend"], function (i18next, i18nextHttpBacke
6989
console.error("i18next init failed:", err);
7090
}
7191
window.i18next = i18next;
72-
resolve(i18next);
92+
resolve(i18next);
7393
}
7494
);
7595
});
@@ -101,4 +121,4 @@ requirejs(["i18next", "i18nextHttpBackend"], function (i18next, i18nextHttpBacke
101121
}
102122

103123
main();
104-
});
124+
});

js/p5-adapter.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* global define, window */
2+
define(["p5.min"], function (p5) {
3+
console.log("p5-adapter: p5 loaded");
4+
if (!window.p5 && p5) {
5+
window.p5 = p5;
6+
}
7+
if (window.Tone) {
8+
console.log("p5-adapter: Saving OriginalTone");
9+
window.OriginalTone = window.Tone;
10+
} else {
11+
console.warn("p5-adapter: window.Tone not found!");
12+
}
13+
14+
// Save original AudioContext constructors to prevent p5.sound from hijacking them
15+
if (window.AudioContext) {
16+
window.OriginalAudioContext = window.AudioContext;
17+
}
18+
if (window.webkitAudioContext) {
19+
window.OriginalWebkitAudioContext = window.webkitAudioContext;
20+
}
21+
22+
// Save original connect just in case
23+
if (window.AudioNode && window.AudioNode.prototype) {
24+
window.OriginalAudioNodeConnect = window.AudioNode.prototype.connect;
25+
}
26+
27+
return p5;
28+
});

js/p5-sound-adapter.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/* global define, window */
2+
define(["p5.sound.min"], function () {
3+
console.log("p5-sound-adapter: p5.sound loaded");
4+
5+
// Restore AudioContext if it was overwritten
6+
if (window.OriginalAudioContext && window.AudioContext !== window.OriginalAudioContext) {
7+
console.log("p5-sound-adapter: Restoring AudioContext");
8+
window.AudioContext = window.OriginalAudioContext;
9+
}
10+
if (
11+
window.OriginalWebkitAudioContext &&
12+
window.webkitAudioContext !== window.OriginalWebkitAudioContext
13+
) {
14+
console.log("p5-sound-adapter: Restoring webkitAudioContext");
15+
window.webkitAudioContext = window.OriginalWebkitAudioContext;
16+
}
17+
18+
// Restore Tone.js
19+
if (window.OriginalTone) {
20+
if (window.Tone !== window.OriginalTone) {
21+
console.log("p5-sound-adapter: Restoring MusicBlocks Tone.js");
22+
window.Tone = window.OriginalTone;
23+
} else {
24+
console.log("p5-sound-adapter: Tone was not overwritten by p5.sound");
25+
}
26+
} else {
27+
console.warn("p5-sound-adapter: No OriginalTone to restore!");
28+
}
29+
30+
// Fix AudioNode.prototype.connect return value
31+
// We force this patch because p5.sound is known to break chaining,
32+
// and sometimes the reference check fails (e.g. if p5.sound wraps it in a way that preserves identity or if we missed the timing).
33+
// The error "s.connect(...) is undefined" confirms we MUST ensure a return value.
34+
if (window.AudioNode && window.AudioNode.prototype) {
35+
var currentConnect = window.AudioNode.prototype.connect;
36+
37+
// Avoid double-patching if we already did it
38+
if (!currentConnect.isP5AdapterPatched) {
39+
console.log(
40+
"p5-sound-adapter: Forcing patch of AudioNode.prototype.connect to support chaining"
41+
);
42+
43+
window.AudioNode.prototype.connect = function () {
44+
var result = currentConnect.apply(this, arguments);
45+
// If the result is undefined (which breaks Tone.js chaining), return the destination (arguments[0])
46+
if (result === undefined) {
47+
return arguments[0];
48+
}
49+
return result;
50+
};
51+
52+
// Mark as patched
53+
window.AudioNode.prototype.connect.isP5AdapterPatched = true;
54+
} else {
55+
console.log("p5-sound-adapter: AudioNode.prototype.connect already patched");
56+
}
57+
}
58+
});

0 commit comments

Comments
 (0)