| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of web_audio; |
| |
| $(ANNOTATIONS)$(NATIVESPEC)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS { |
| $!MEMBERS |
| factory AudioContext() => JS('AudioContext', |
| 'new (window.AudioContext || window.webkitAudioContext)()'); |
| |
| GainNode createGain() { |
| if (JS('bool', '#.createGain !== undefined', this)) { |
| return JS('GainNode', '#.createGain()', this); |
| } else { |
| return JS('GainNode', '#.createGainNode()', this); |
| } |
| } |
| |
| ScriptProcessorNode createScriptProcessor([int$NULLABLE bufferSize, |
| int$NULLABLE numberOfInputChannels, |
| int$NULLABLE numberOfOutputChannels]) { |
| var function = JS( |
| '=Object', |
| '#.createScriptProcessor || ' |
| '#.createJavaScriptNode', |
| this, |
| this); |
| if (numberOfOutputChannels != null) { |
| return JS('ScriptProcessorNode', '#.call(#, #, #, #)', function, this, |
| bufferSize, numberOfInputChannels, numberOfOutputChannels); |
| } else if (numberOfInputChannels != null) { |
| return JS('ScriptProcessorNode', '#.call(#, #, #)', function, this, |
| bufferSize, numberOfInputChannels); |
| } else if (bufferSize != null) { |
| return JS( |
| 'ScriptProcessorNode', '#.call(#, #)', function, this, bufferSize); |
| } else { |
| return JS( |
| 'ScriptProcessorNode', '#.call(#)', function, this); |
| } |
| } |
| |
| Future<AudioBuffer> decodeAudioData(ByteBuffer audioData, |
| [DecodeSuccessCallback$NULLABLE successCallback, |
| DecodeErrorCallback$NULLABLE errorCallback]) { |
| // Both callbacks need to be provided if they're being used. |
| assert((successCallback == null) == (errorCallback == null)); |
| // `decodeAudioData` can exist either in the older callback syntax or the |
| // newer `Promise`-based syntax that also accepts callbacks. In the former, |
| // we synthesize a `Future` to be consistent. |
| // For more details: |
| // https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/decodeAudioData |
| // https://www.w3.org/TR/webaudio/#dom-baseaudiocontext-decodeaudiodata |
| final completer = Completer<Object>(); |
| var errorInCallbackIsNull = false; |
| |
| void success(AudioBuffer decodedData) { |
| completer.complete(decodedData); |
| successCallback$NULLASSERT.call(decodedData); |
| } |
| |
| final nullErrorString = |
| '[AudioContext.decodeAudioData] completed with a null error.'; |
| |
| void error(DomException$NULLABLE error) { |
| // Safari has a bug where it may return null for the error callback. In |
| // the case where the Safari version still returns a `Promise` and the |
| // error is not null after the `Promise` is finished, the error callback |
| // is called instead in the `Promise`'s `catch` block. Otherwise, and in |
| // the case where a `Promise` is not returned by the API at all, the |
| // callback never gets called (for backwards compatibility, it can not |
| // accept null). Instead, the `Future` completes with a custom string, |
| // indicating that null was given. |
| // https://github.com/mdn/webaudio-examples/issues/5 |
| if (error != null) { |
| // Note that we `complete` and not `completeError`. This is to make sure |
| // that errors in the `Completer` are not thrown if the call gets back |
| // a `Promise`. |
| completer.complete(error); |
| errorCallback$NULLASSERT.call(error); |
| } else { |
| completer.complete(nullErrorString); |
| errorInCallbackIsNull = true; |
| } |
| } |
| |
| var decodeResult; |
| if (successCallback == null) { |
| decodeResult = |
| JS("creates:AudioBuffer;", "#.decodeAudioData(#)", this, audioData); |
| } else { |
| decodeResult = JS( |
| "creates:AudioBuffer;", |
| "#.decodeAudioData(#, #, #)", |
| this, |
| audioData, |
| convertDartClosureToJS(success, 1), |
| convertDartClosureToJS(error, 1)); |
| } |
| |
| if (decodeResult != null) { |
| // Promise-based syntax. |
| return promiseToFuture<AudioBuffer>(decodeResult).catchError((error) { |
| // If the error was null in the callback, but no longer is now that the |
| // `Promise` is finished, call the error callback. If it's still null, |
| // throw the error string. This is to handle the aforementioned bug in |
| // Safari. |
| if (errorInCallbackIsNull) { |
| if (error != null) { |
| errorCallback?.call(error); |
| } else { |
| throw nullErrorString; |
| } |
| } |
| throw error; |
| }); |
| } |
| |
| // Callback-based syntax. We use the above completer to synthesize a |
| // `Future` from the callback values. Since we don't use `completeError` |
| // above, `then` is used to simulate an error. |
| return completer.future.then((value) { |
| if (value is AudioBuffer) return value; |
| throw value; |
| }); |
| } |
| } |