View Javadoc
1   /*
2    * Copyright (c) 2002-2025 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  @JsxClass
37  public class BaseAudioContext extends EventTarget {
38  
39      /**
40       * Creates an instance.
41       */
42      @Override
43      @JsxConstructor
44      public void jsConstructor() {
45          throw JavaScriptEngine.typeErrorIllegalConstructor();
46      }
47  
48      /**
49       * @return a new AudioBufferSourceNode, which can be used to
50       *         play audio data contained within an AudioBuffer object.
51       */
52      @JsxFunction
53      public AudioBufferSourceNode createBufferSource() {
54          final AudioBufferSourceNode node = new AudioBufferSourceNode();
55          node.setParentScope(getParentScope());
56          node.setPrototype(getPrototype(node.getClass()));
57          return node;
58      }
59  
60      /**
61       * @return new, empty AudioBuffer object, which can then be
62       *         populated by data, and played via an AudioBufferSourceNode.
63       */
64      @JsxFunction
65      public AudioBuffer createBuffer() {
66          final AudioBuffer node = new AudioBuffer();
67          node.setParentScope(getParentScope());
68          node.setPrototype(getPrototype(node.getClass()));
69          return node;
70      }
71  
72      /**
73       * @return a GainNode, which can be used to control the overall gain (or volume) of the audio graph.
74       */
75      @JsxFunction
76      public GainNode createGain() {
77          final GainNode node = new GainNode();
78          node.setParentScope(getParentScope());
79          node.setPrototype(getPrototype(node.getClass()));
80          node.jsConstructor(this);
81          return node;
82      }
83  
84      /**
85       * The decodeAudioData() method of the BaseAudioContext Interface is used to asynchronously
86       * decode audio file data contained in an ArrayBuffer. In this case the ArrayBuffer is
87       * loaded from XMLHttpRequest and FileReader.
88       * The decoded AudioBuffer is resampled to the AudioContext's sampling rate,
89       * then passed to a callback or promise.
90       * @param buffer An ArrayBuffer containing the audio data to be decoded, usually grabbed
91       *        from XMLHttpRequest, WindowOrWorkerGlobalScope.fetch() or FileReader
92       * @param success A callback function to be invoked when the decoding successfully finishes.
93       *        The single argument to this callback is an AudioBuffer representing the decodedData
94       *        (the decoded PCM audio data). Usually you'll want to put the decoded data into
95       *        an AudioBufferSourceNode, from which it can be played and manipulated how you want.
96       * @param error An optional error callback, to be invoked if an error occurs
97       *        when the audio data is being decoded.
98       * @return the promise or null
99       */
100     @JsxFunction
101     public NativePromise decodeAudioData(final NativeArrayBuffer buffer, final Function success, final Function error) {
102         final Window window = getWindow();
103         final HtmlPage owningPage = (HtmlPage) window.getDocument().getPage();
104         final JavaScriptEngine jsEngine =
105                 (JavaScriptEngine) window.getWebWindow().getWebClient().getJavaScriptEngine();
106 
107         final DOMException domException = new DOMException(
108                 "decodeAudioData not supported by HtmlUnit", DOMException.NOT_SUPPORTED_ERR);
109         domException.setParentScope(window);
110         domException.setPrototype(window.getPrototype(DOMException.class));
111 
112         if (error != null) {
113             jsEngine.addPostponedAction(new PostponedAction(owningPage, "BaseAudioContext.decodeAudioData") {
114                 @Override
115                 public void execute() {
116                     jsEngine.callFunction(owningPage, error, getParentScope(), BaseAudioContext.this,
117                             new Object[] {domException});
118                 }
119             });
120             return null;
121         }
122 
123         return setupRejectedPromise(() -> domException);
124     }
125 }