1 /*
2 * Copyright (c) 2002-2026 Gargoyle Software Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 * https://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package org.htmlunit.javascript.host.media;
16
17 import org.htmlunit.corejs.javascript.Function;
18 import org.htmlunit.corejs.javascript.NativePromise;
19 import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBuffer;
20 import org.htmlunit.html.HtmlPage;
21 import org.htmlunit.javascript.JavaScriptEngine;
22 import org.htmlunit.javascript.PostponedAction;
23 import org.htmlunit.javascript.configuration.JsxClass;
24 import org.htmlunit.javascript.configuration.JsxConstructor;
25 import org.htmlunit.javascript.configuration.JsxFunction;
26 import org.htmlunit.javascript.host.Window;
27 import org.htmlunit.javascript.host.dom.DOMException;
28 import org.htmlunit.javascript.host.event.EventTarget;
29
30 /**
31 * A JavaScript object for {@code BaseAudioContext}.
32 *
33 * @author Ahmed Ashour
34 * @author Ronald Brill
35 *
36 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext">MDN Documentation</a>
37 */
38 @JsxClass
39 public class BaseAudioContext extends EventTarget {
40
41 /**
42 * Creates an instance.
43 */
44 @Override
45 @JsxConstructor
46 public void jsConstructor() {
47 throw JavaScriptEngine.typeErrorIllegalConstructor();
48 }
49
50 /**
51 * Creates a new AudioBufferSourceNode, which can be used to play audio data contained within an AudioBuffer object.
52 * @return a new AudioBufferSourceNode, which can be used to
53 * play audio data contained within an AudioBuffer object.
54 */
55 @JsxFunction
56 public AudioBufferSourceNode createBufferSource() {
57 final AudioBufferSourceNode node = new AudioBufferSourceNode();
58 node.setParentScope(getParentScope());
59 node.setPrototype(getPrototype(node.getClass()));
60 return node;
61 }
62
63 /**
64 * Creates a new, empty AudioBuffer object, which can then be
65 * populated by data, and played via an AudioBufferSourceNode.
66 * @return new, empty AudioBuffer object, which can then be
67 * populated by data, and played via an AudioBufferSourceNode.
68 */
69 @JsxFunction
70 public AudioBuffer createBuffer() {
71 final AudioBuffer node = new AudioBuffer();
72 node.setParentScope(getParentScope());
73 node.setPrototype(getPrototype(node.getClass()));
74 return node;
75 }
76
77 /**
78 * Creates a GainNode, which can be used to control the overall gain (or volume) of the audio graph.
79 * @return a GainNode, which can be used to control the overall gain (or volume) of the audio graph.
80 */
81 @JsxFunction
82 public GainNode createGain() {
83 final GainNode node = new GainNode();
84 node.setParentScope(getParentScope());
85 node.setPrototype(getPrototype(node.getClass()));
86 node.jsConstructor(this);
87 return node;
88 }
89
90 /**
91 * The decodeAudioData() method of the BaseAudioContext Interface is used to asynchronously
92 * decode audio file data contained in an ArrayBuffer. In this case the ArrayBuffer is
93 * loaded from XMLHttpRequest and FileReader.
94 * The decoded AudioBuffer is resampled to the AudioContext's sampling rate,
95 * then passed to a callback or promise.
96 * @param buffer An ArrayBuffer containing the audio data to be decoded, usually grabbed
97 * from XMLHttpRequest, WindowOrWorkerGlobalScope.fetch() or FileReader
98 * @param success A callback function to be invoked when the decoding successfully finishes.
99 * The single argument to this callback is an AudioBuffer representing the decodedData
100 * (the decoded PCM audio data). Usually you'll want to put the decoded data into
101 * an AudioBufferSourceNode, from which it can be played and manipulated how you want.
102 * @param error An optional error callback, to be invoked if an error occurs
103 * when the audio data is being decoded.
104 * @return the promise or null
105 */
106 @JsxFunction
107 public NativePromise decodeAudioData(final NativeArrayBuffer buffer, final Function success, final Function error) {
108 final Window window = getWindow();
109 final HtmlPage owningPage = (HtmlPage) window.getDocument().getPage();
110 final JavaScriptEngine jsEngine =
111 (JavaScriptEngine) window.getWebWindow().getWebClient().getJavaScriptEngine();
112
113 final DOMException domException = new DOMException(
114 "decodeAudioData not supported by HtmlUnit", DOMException.NOT_SUPPORTED_ERR);
115 domException.setParentScope(getParentScope());
116 domException.setPrototype(window.getPrototype(DOMException.class));
117
118 if (error != null) {
119 jsEngine.addPostponedAction(new PostponedAction(owningPage, "BaseAudioContext.decodeAudioData") {
120 @Override
121 public void execute() {
122 jsEngine.callFunction(owningPage, error, getParentScope(), BaseAudioContext.this,
123 new Object[] {domException});
124 }
125 });
126 return null;
127 }
128
129 return setupRejectedPromise(() -> domException);
130 }
131 }