diff --git a/Config/eslint.json b/Config/eslint.json
index f8fb8cb..b4e11da 100644
--- a/Config/eslint.json
+++ b/Config/eslint.json
@@ -54,12 +54,14 @@
"C_WebView": "writable",
"BinaryReader": "writable",
"Bitmap": "writable",
+ "BuiltinAudioDecoder": "writable",
"Button": "writable",
"Canvas": "writable",
"Channels": "writable",
"CheckButton": "writable",
"Color": "writable",
"CRC32": "writable",
+ "Decoder": "writable",
"EditBox": "writable",
"Enum": "writable",
"FieldSet": "writable",
diff --git a/Core/API/WebAudio/BuiltinAudioDecoder.js b/Core/API/WebAudio/BuiltinAudioDecoder.js
new file mode 100644
index 0000000..04ac964
--- /dev/null
+++ b/Core/API/WebAudio/BuiltinAudioDecoder.js
@@ -0,0 +1,12 @@
+// This doesn't do any actual decoding; it merely serves to utilize the Resource cache for audio playback
+// The audio engine supports no caching mechanism of its own, so we wrap any calls to disk and serve Resources instead
+class BuiltinAudioDecoder extends Decoder {
+ fileTypes = { mp3: true, ogg: true, wav: true };
+ // No need to do any decoding as these audio formats are supported natively
+ decode(resource) {
+ return resource;
+ }
+ getSupportedFileTypes() {
+ return this.fileTypes;
+ }
+}
diff --git a/Core/APIs/C_Decoding.js b/Core/APIs/C_Decoding.js
index 502e593..a41b9c9 100644
--- a/Core/APIs/C_Decoding.js
+++ b/Core/APIs/C_Decoding.js
@@ -24,15 +24,17 @@ const C_Decoding = {
return true;
},
addDecoder(decoder) {
- const fileType = decoder.getSupportedFileType();
+ const fileTypes = decoder.getSupportedFileTypes();
- if (this.registeredDecoders[fileType]) {
- NOTICE(format("Failed to register decoder for file type %s (already registered)", fileType));
- return;
- }
+ for (const fileType of Object.keys(fileTypes)) {
+ if (this.registeredDecoders[fileType]) {
+ NOTICE(format("Failed to register decoder for file type %s (already registered)", fileType));
+ return;
+ }
- this.registeredDecoders[fileType] = decoder;
- DEBUG(format("Registered new decoder for file type *.%s", fileType.toUpperCase()));
+ this.registeredDecoders[fileType] = decoder;
+ DEBUG(format("Registered new decoder for file type *.%s", fileType.toUpperCase()));
+ }
},
decodeFile(filePath) {
const resource = C_Resources.load(filePath);
diff --git a/Core/Builtins/Decoder.js b/Core/Builtins/Decoder.js
new file mode 100644
index 0000000..e7d4fce
--- /dev/null
+++ b/Core/Builtins/Decoder.js
@@ -0,0 +1,12 @@
+class Decoder {
+ fileTypes = {};
+ constructor(decodingFunction) {
+ this.decode = decodingFunction || this.decode; // NO-OP as a safe default
+ }
+ getSupportedFileTypes() {
+ return this.fileTypes;
+ }
+ decode(resource) {
+ return resource;
+ }
+}
diff --git a/Core/Classes/Decoder.js b/Core/Classes/Decoder.js
deleted file mode 100644
index 554aee3..0000000
--- a/Core/Classes/Decoder.js
+++ /dev/null
@@ -1,12 +0,0 @@
-class Decoder {
- constructor(fileType, decodingFunction) {
- this.fileType = fileType;
- this.decodingFunction = decodingFunction;
- }
- getSupportedFileType() {
- return this.fileType;
- }
- decode(resource) {
- return this.decodingFunction(resource);
- }
-}
diff --git a/Tests/API/C_WebAudio/BuiltinAudioDecoder.js b/Tests/API/C_WebAudio/BuiltinAudioDecoder.js
new file mode 100644
index 0000000..b6a6207
--- /dev/null
+++ b/Tests/API/C_WebAudio/BuiltinAudioDecoder.js
@@ -0,0 +1,27 @@
+describe("BuiltinAudioDecoder", () => {
+ const decoder = new BuiltinAudioDecoder();
+ it("should be exported into the global environment", () => {
+ assertEquals(decoder.constructor.name, "BuiltinAudioDecoder");
+ });
+
+ let exportedApiSurface = ["getSupportedFileTypes", "decode"];
+
+ exportedApiSurface.forEach((namedExport) => {
+ it("should export function " + namedExport, () => {
+ assertEquals(typeof decoder[namedExport], "function");
+ });
+ });
+
+ const supportedFormats = decoder.getSupportedFileTypes();
+ it("should indicate that MP3 files can be decoded", () => {
+ assertTrue(supportedFormats["mp3"]);
+ });
+
+ it("should indicate that OGG files can be decoded", () => {
+ assertTrue(supportedFormats["ogg"]);
+ });
+
+ it("should indicate that WAV files can be decoded", () => {
+ assertTrue(supportedFormats["wav"]);
+ });
+});
diff --git a/Tests/Builtins/Decoder.js b/Tests/Builtins/Decoder.js
new file mode 100644
index 0000000..a8e4fd3
--- /dev/null
+++ b/Tests/Builtins/Decoder.js
@@ -0,0 +1,16 @@
+describe("Decoder", () => {
+ const decoder = new Decoder();
+ it("should be exported into the global environment", () => {
+ assertEquals(decoder.constructor.name, "Decoder");
+ });
+
+ let exportedApiSurface = ["getSupportedFileTypes", "decode"];
+
+ exportedApiSurface.forEach((namedExport) => {
+ it("should export function " + namedExport, () => {
+ assertEquals(typeof decoder[namedExport], "function");
+ });
+ });
+
+ // Since this is just an interface that needs to be implemented, there's no implementation to test
+});
diff --git a/Tests/run-renderer-tests.js b/Tests/run-renderer-tests.js
index 65f2316..27f33a2 100644
--- a/Tests/run-renderer-tests.js
+++ b/Tests/run-renderer-tests.js
@@ -1,11 +1,18 @@
const testSuites = {
SharedConstants: ["SharedConstants/Aliases.js", "SharedConstants/Paths.js"],
- Builtins: ["Builtins/Assertions.js", "Builtins/LocalCacheTests.js", "Builtins/UniqueID.js", "Builtins/Validators.js"],
+ Builtins: [
+ "Builtins/Assertions.js",
+ "Builtins/LocalCacheTests.js",
+ "Builtins/Decoder.js",
+ "Builtins/UniqueID.js",
+ "Builtins/Validators.js",
+ ],
C_Settings: [
"API/C_Settings/validate.js",
"API/C_Settings/validateDefaultSettings.js",
"API/C_Settings/validateUserSettings.js",
],
+ C_WebAudio: ["API/C_WebAudio/BuiltinAudioDecoder.js"],
};
for (const namespace in testSuites) {
diff --git a/index.html b/index.html
index 7418e9e..a676f1d 100644
--- a/index.html
+++ b/index.html
@@ -65,8 +65,6 @@
-
-
@@ -140,6 +138,8 @@
StartWebClient();