/* * Copyright 2010 Fred Sauer * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.allen_sauer.gwt.voices.client; import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_NOT_SUPPORTED; import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORTED_NOT_READY; import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORT_NOT_KNOWN; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.dom.client.Element; import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport; /** * Sound object using the Web Audio API. */ public class WebAudioSound extends AbstractSound { // CHECKSTYLE_JAVADOC_OFF private String mimeType; private Element voice; private static Element audioContext; private JavaScriptObject buffer; private int volume; private boolean looping; public static MimeTypeSupport getMimeTypeSupport(String mimeType) { if (audioContext == null) { return MimeTypeSupport.MIME_TYPE_NOT_SUPPORTED; } return Html5Sound.getMimeTypeSupport(mimeType); } public WebAudioSound(String mimeType, String url, boolean streaming, boolean crossOrigin) { super(mimeType, url, streaming, crossOrigin); this.mimeType = mimeType; try { createVoice(url, crossOrigin); } catch (Throwable e) { setLoadState(LOAD_STATE_NOT_SUPPORTED); } MimeTypeSupport mimeTypeSupport = getMimeTypeSupport(mimeType); switch (mimeTypeSupport) { case MIME_TYPE_SUPPORT_READY: setLoadState(LOAD_STATE_SUPPORTED_NOT_READY); break; case MIME_TYPE_NOT_SUPPORTED: setLoadState(LOAD_STATE_NOT_SUPPORTED); break; case MIME_TYPE_SUPPORT_UNKNOWN: setLoadState(LOAD_STATE_SUPPORT_NOT_KNOWN); break; case MIME_TYPE_SUPPORT_NOT_READY: setLoadState(LOAD_STATE_SUPPORTED_NOT_READY); break; default: throw new IllegalArgumentException("unknown MIME type support " + mimeTypeSupport); } } static { audioContext = createAudioContext(); } private static native Element createAudioContext() /*-{ try { return new AudioContext(); } catch (ignore) { } return null; }-*/; private native void createVoice(String url, boolean crossOrigin) /*-{ var context = @com.allen_sauer.gwt.voices.client.WebAudioSound::audioContext; // TODO: Implement XDomainRequest for IE var request = new $wnd.XMLHttpRequest(); request.open("GET", url, true); request.responseType = "arraybuffer"; var self = this; request.onload = function() { context.decodeAudioData(request.response, function onSuccess(decodedBuffer) { self.@com.allen_sauer.gwt.voices.client.WebAudioSound::buffer = decodedBuffer; self.@com.allen_sauer.gwt.voices.client.WebAudioSound::soundLoaded()(); }, function onFailure() { self.@com.allen_sauer.gwt.voices.client.WebAudioSound::soundLoadFailed()(); }); } request.send(); }-*/; private void soundLoadFailed() { setLoadState(LoadState.LOAD_STATE_NOT_SUPPORTED); } private void soundLoaded() { setLoadState(LoadState.LOAD_STATE_SUPPORTED_AND_READY); } @Override public int getBalance() { // TODO(fredsa): Auto-generated method stub return 0; } @Override public boolean getLooping() { return looping; } @Override public int getVolume() { return volume; } @Override public native boolean play() /*-{ var buffer = this.@com.allen_sauer.gwt.voices.client.WebAudioSound::buffer; if (buffer == null) { // XHR has not yet returned return false; } this.@com.allen_sauer.gwt.voices.client.WebAudioSound::stop()(); var context = @com.allen_sauer.gwt.voices.client.WebAudioSound::audioContext; var voice = context.createBufferSource(); this.@com.allen_sauer.gwt.voices.client.WebAudioSound::voice = voice; if (this.@com.allen_sauer.gwt.voices.client.WebAudioSound::looping) { voice.loop = true; } var node = voice; var volume = this.@com.allen_sauer.gwt.voices.client.WebAudioSound::volume; if (volume != @com.allen_sauer.gwt.voices.client.SoundController::DEFAULT_VOLUME) { var gainNode = context.createGain(); gainNode.gain.value = volume / @com.allen_sauer.gwt.voices.client.SoundController::DEFAULT_VOLUME; node.connect(gainNode); node = gainNode; } node.connect(context.destination); voice.buffer = buffer; voice.start(context.currentTime); // TODO Replace setTimeout() once https://bugs.webkit.org/show_bug.cgi?id=71942 is fixed if (!voice.loop) { var self = this; setTimeout( function() { self.@com.allen_sauer.gwt.voices.client.WebAudioSound::plackbackCompleted()(); }, voice.buffer.duration * 1000); } return true; }-*/; private void plackbackCompleted() { soundHandlerCollection.fireOnPlaybackComplete(this); } @Override public void setBalance(int balance) { // TODO(fredsa): Auto-generated method stub } @Override public void setLooping(boolean looping) { this.looping = looping; } @Override public void setVolume(int volume) { this.volume = volume; } @Override public native void stop() /*-{ var context = @com.allen_sauer.gwt.voices.client.WebAudioSound::audioContext; var voice = this.@com.allen_sauer.gwt.voices.client.WebAudioSound::voice; if (voice == null) { return; } voice.stop(context.currentTime); this.@com.allen_sauer.gwt.voices.client.WebAudioSound::voice = null; }-*/; @Override public SoundType getSoundType() { return SoundType.WEB_AUDIO; } }