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(); + +