View Javadoc
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 }