| // 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; | |
| }); | |
| } | |
| } |