--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client;\r
+\r
+import com.allen_sauer.gwt.voices.client.handler.SoundHandler;\r
+import com.allen_sauer.gwt.voices.client.handler.SoundHandlerCollection;\r
+import com.allen_sauer.gwt.voices.client.handler.SoundLoadStateChangeEvent;\r
+\r
+abstract class AbstractSound implements Sound {\r
+ private static final LoadState INITIAL_LOAD_STATE = LoadState.LOAD_STATE_UNINITIALIZED;\r
+ protected final SoundHandlerCollection soundHandlerCollection = new SoundHandlerCollection();\r
+\r
+ private final boolean crossOrigin;\r
+ private LoadState loadState = INITIAL_LOAD_STATE;\r
+ private final String mimeType;\r
+ private final boolean streaming;\r
+ private final String url;\r
+\r
+ public AbstractSound(String mimeType, String url, boolean streaming, boolean crossOrigin) {\r
+ this.mimeType = mimeType;\r
+ this.url = url;\r
+ this.streaming = streaming;\r
+ this.crossOrigin = crossOrigin;\r
+ }\r
+\r
+ @Override\r
+ public final void addEventHandler(SoundHandler handler) {\r
+ soundHandlerCollection.add(handler);\r
+ if (loadState != INITIAL_LOAD_STATE) {\r
+ handler.onSoundLoadStateChange(new SoundLoadStateChangeEvent(this));\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public final LoadState getLoadState() {\r
+ return loadState;\r
+ }\r
+\r
+ @Override\r
+ public final String getMimeType() {\r
+ return mimeType;\r
+ }\r
+\r
+ @Override\r
+ public abstract SoundType getSoundType();\r
+\r
+ @Override\r
+ public final String getUrl() {\r
+ return url;\r
+ }\r
+\r
+ public boolean isCrossOrigin() {\r
+ return crossOrigin;\r
+ }\r
+\r
+ public final boolean isStreaming() {\r
+ return streaming;\r
+ }\r
+\r
+ @Override\r
+ public final void removeEventHandler(SoundHandler handler) {\r
+ soundHandlerCollection.remove(handler);\r
+ }\r
+\r
+ public final void setLoadState(LoadState loadState) {\r
+ if (loadState != this.loadState) {\r
+ this.loadState = loadState;\r
+ if (loadState != INITIAL_LOAD_STATE) {\r
+ soundHandlerCollection.fireOnSoundLoadStateChange(this);\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public final String toString() {\r
+ return getSoundType() + "(\"" + mimeType + "\", \"" + url + "\", "\r
+ + (isStreaming() ? "streaming" : "not streaming") + ", "\r
+ + (isCrossOrigin() ? "cross origin" : "same origin") + ")";\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client;\r
+\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORTED_AND_READY;\r
+\r
+import com.google.gwt.core.client.Scheduler;\r
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;\r
+\r
+import com.allen_sauer.gwt.voices.client.ui.VoicesMovie;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * <a href= 'http://www.adobe.com/products/flashplayer/'>Adobe Flash Player</a> based\r
+ * sound.\r
+ */\r
+public class FlashSound extends AbstractSound {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ private static ArrayList<FlashSound> soundList = new ArrayList<FlashSound>();\r
+\r
+ @SuppressWarnings("unused")\r
+ private static void playbackCompleted(final int index) {\r
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {\r
+ @Override\r
+ public void execute() {\r
+ soundList.get(index).playbackCompleted();\r
+ }\r
+ });\r
+ }\r
+\r
+ @SuppressWarnings("unused")\r
+ private static void soundLoaded(final int index) {\r
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {\r
+ @Override\r
+ public void execute() {\r
+ soundList.get(index).soundLoaded();\r
+ }\r
+ });\r
+ }\r
+\r
+ private int balance = SoundController.DEFAULT_BALANCE;\r
+ private boolean looping = SoundController.DEFAULT_LOOPING;\r
+ private final int soundNumber;\r
+ private boolean soundRegistered = false;\r
+ private final VoicesMovie voicesMovie;\r
+ private int volume = SoundController.DEFAULT_VOLUME;\r
+\r
+ public FlashSound(String mimeType, String url, boolean streaming, boolean crossOrigin,\r
+ VoicesMovie voicesMovie) {\r
+ super(mimeType, url, streaming, crossOrigin);\r
+ this.voicesMovie = voicesMovie;\r
+ soundNumber = soundList.size();\r
+ soundList.add(this);\r
+ if (streaming) {\r
+ setLoadState(LOAD_STATE_SUPPORTED_AND_READY);\r
+ } else {\r
+ registerSound();\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public int getBalance() {\r
+ return balance;\r
+ }\r
+\r
+ @Override\r
+ public boolean getLooping() {\r
+ return looping;\r
+ }\r
+\r
+ public int getSoundNumber() {\r
+ return soundNumber;\r
+ }\r
+\r
+ @Override\r
+ public SoundType getSoundType() {\r
+ return SoundType.FLASH;\r
+ }\r
+\r
+ @Override\r
+ public int getVolume() {\r
+ return volume;\r
+ }\r
+\r
+ @Override\r
+ public boolean play() {\r
+ registerSound();\r
+ if (getLoadState() == LOAD_STATE_SUPPORTED_AND_READY) {\r
+ // true indicates the sound was played\r
+ return voicesMovie.playSound(soundNumber);\r
+ }\r
+ // sound was not played, return false\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public void setBalance(int balance) {\r
+ assert balance >= -100;\r
+ assert balance <= 100;\r
+ this.balance = balance;\r
+ if (getLoadState() == LOAD_STATE_SUPPORTED_AND_READY) {\r
+ voicesMovie.setBalance(soundNumber, balance);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void setLooping(boolean looping) {\r
+ this.looping = looping;\r
+ if (getLoadState() == LOAD_STATE_SUPPORTED_AND_READY) {\r
+ voicesMovie.setLooping(soundNumber, looping);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void setVolume(int volume) {\r
+ assert volume >= 0;\r
+ assert volume <= 100;\r
+ this.volume = volume;\r
+ if (getLoadState() == LOAD_STATE_SUPPORTED_AND_READY) {\r
+ voicesMovie.setVolume(soundNumber, volume);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void stop() {\r
+ if (getLoadState() == LOAD_STATE_SUPPORTED_AND_READY) {\r
+ voicesMovie.stopSound(soundNumber);\r
+ }\r
+ }\r
+\r
+ protected void playbackCompleted() {\r
+ soundHandlerCollection.fireOnPlaybackComplete(this);\r
+ }\r
+\r
+ protected void soundLoaded() {\r
+ setLoadState(LOAD_STATE_SUPPORTED_AND_READY);\r
+ if (volume != SoundController.DEFAULT_VOLUME) {\r
+ voicesMovie.setVolume(soundNumber, volume);\r
+ }\r
+ if (balance != SoundController.DEFAULT_BALANCE) {\r
+ voicesMovie.setBalance(soundNumber, balance);\r
+ }\r
+ if (looping != SoundController.DEFAULT_LOOPING) {\r
+ voicesMovie.setLooping(soundNumber, looping);\r
+ }\r
+ }\r
+\r
+ private void registerSound() {\r
+ if (!soundRegistered) {\r
+ voicesMovie.registerSound(this);\r
+ soundRegistered = true;\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client;\r
+\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_NOT_SUPPORTED;\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORTED_MAYBE_READY;\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORT_NOT_KNOWN;\r
+\r
+import com.google.gwt.dom.client.AudioElement;\r
+import com.google.gwt.event.dom.client.EndedEvent;\r
+import com.google.gwt.event.dom.client.EndedHandler;\r
+import com.google.gwt.event.shared.HandlerRegistration;\r
+import com.google.gwt.media.client.Audio;\r
+import com.google.gwt.user.client.ui.RootPanel;\r
+\r
+import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport;\r
+\r
+/**\r
+ * Sound object representing sounds which can be played back via HTML5 audio.\r
+ */\r
+public class Html5Sound extends AbstractSound {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ /**\r
+ * @param mimeType the requested MIME type and optional codec according to RFC 4281\r
+ * @return the level of support for the provided MIME type\r
+ */\r
+ public static MimeTypeSupport getMimeTypeSupport(String mimeType) {\r
+ if (!Audio.isSupported()) {\r
+ return MimeTypeSupport.MIME_TYPE_NOT_SUPPORTED;\r
+ }\r
+ String support = Audio.createIfSupported().getAudioElement().canPlayType(mimeType);\r
+ assert support != null;\r
+ if (AudioElement.CAN_PLAY_PROBABLY.equals(support)) {\r
+ return MimeTypeSupport.MIME_TYPE_SUPPORT_READY;\r
+ }\r
+ if (AudioElement.CAN_PLAY_MAYBE.equals(support)) {\r
+ return MimeTypeSupport.MIME_TYPE_SUPPORT_READY;\r
+ }\r
+ return MimeTypeSupport.MIME_TYPE_SUPPORT_UNKNOWN;\r
+ }\r
+\r
+ private Audio audio;\r
+\r
+ private EndedHandler endedHandler = new EndedHandler() {\r
+ @Override\r
+ public void onEnded(EndedEvent event) {\r
+ soundHandlerCollection.fireOnPlaybackComplete(Html5Sound.this);\r
+ }\r
+ };\r
+\r
+ private HandlerRegistration endedRegistration;\r
+\r
+ /**\r
+ * @param mimeType the requested MIME type and optional codec according to RFC 4281\r
+ * @param url the URL of the audio resource\r
+ * @param streaming whether or not to stream the content, although currently ignored\r
+ * @param crossOrigin whether or not the content is to be accessed from a different origin\r
+ */\r
+ public Html5Sound(String mimeType, String url, boolean streaming, boolean crossOrigin) {\r
+ super(mimeType, url, streaming, crossOrigin);\r
+\r
+ createAudioElement();\r
+\r
+ MimeTypeSupport mimeTypeSupport = getMimeTypeSupport(mimeType);\r
+ switch (mimeTypeSupport) {\r
+ case MIME_TYPE_SUPPORT_READY:\r
+ setLoadState(LOAD_STATE_SUPPORTED_MAYBE_READY);\r
+ break;\r
+ case MIME_TYPE_NOT_SUPPORTED:\r
+ setLoadState(LOAD_STATE_NOT_SUPPORTED);\r
+ break;\r
+ case MIME_TYPE_SUPPORT_UNKNOWN:\r
+ setLoadState(LOAD_STATE_SUPPORT_NOT_KNOWN);\r
+ throw new IllegalArgumentException("unexpected MIME type support " + mimeTypeSupport);\r
+ default:\r
+ throw new IllegalArgumentException("unknown MIME type support " + mimeTypeSupport);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public int getBalance() {\r
+ // not implemented\r
+ return SoundController.DEFAULT_BALANCE;\r
+ }\r
+\r
+ @Override\r
+ public boolean getLooping() {\r
+ return audio.getAudioElement().isLoop();\r
+ }\r
+\r
+ @Override\r
+ public SoundType getSoundType() {\r
+ return SoundType.HTML5;\r
+ }\r
+\r
+ @Override\r
+ public int getVolume() {\r
+ return (int) (audio.getAudioElement().getVolume() * 100d);\r
+ }\r
+\r
+ @Override\r
+ public boolean play() {\r
+ AudioElement elem = audio.getAudioElement();\r
+ elem.pause();\r
+ try {\r
+ // IE9 has been seen to throw an exception here\r
+ elem.setCurrentTime(0);\r
+ } catch (Exception ignore) {\r
+ }\r
+ if (elem.getCurrentTime() != 0) {\r
+ /*\r
+ * Workaround Chrome's inability to play the same audio twice:\r
+ * http://code.google.com/p/chromium/issues/detail?id=71323\r
+ * http://code.google.com/p/chromium/issues/detail?id=75725\r
+ */\r
+ createAudioElement();\r
+ }\r
+ elem.play();\r
+ // best guess is that the sound played, so return true\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public void setBalance(int balance) {\r
+ // not implemented\r
+ }\r
+\r
+ @Override\r
+ public void setLooping(boolean looping) {\r
+ audio.getAudioElement().setLoop(looping);\r
+ }\r
+\r
+ @Override\r
+ public void setVolume(int volume) {\r
+ assert volume >= 0;\r
+ assert volume <= 100;\r
+ audio.getAudioElement().setVolume(volume / 100d);\r
+ }\r
+\r
+ @Override\r
+ public void stop() {\r
+ audio.getAudioElement().pause();\r
+ }\r
+\r
+ private void createAudioElement() {\r
+ if (endedRegistration != null) {\r
+ endedRegistration.removeHandler();\r
+ }\r
+ if (audio != null) {\r
+ // TODO: remove, once DOM attachment no longer required to sink (bitless) events\r
+ audio.removeFromParent();\r
+ }\r
+ assert Audio.isSupported();\r
+ audio = Audio.createIfSupported();\r
+ assert audio != null;\r
+ AudioElement elem = audio.getAudioElement();\r
+ assert elem != null;\r
+\r
+ endedRegistration = audio.addEndedHandler(endedHandler);\r
+\r
+ // TODO: remove, once DOM attachment no longer required to sink (bitless) events\r
+ RootPanel.get().add(audio);\r
+\r
+ if (isCrossOrigin()) {\r
+ elem.setAttribute("crossOrigin", "anonymous");\r
+ }\r
+ elem.setSrc(getUrl());\r
+ }\r
+\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client;\r
+\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_NOT_SUPPORTED;\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORTED_MAYBE_READY;\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORTED_NOT_READY;\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORT_NOT_KNOWN;\r
+\r
+import com.google.gwt.core.client.GWT;\r
+import com.google.gwt.dom.client.Element;\r
+\r
+import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport;\r
+import com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImpl;\r
+\r
+/**\r
+ * Sound object representing sounds which can be played back natively by the browser, i.e. without\r
+ * additional plugins.\r
+ */\r
+public class NativeSound extends AbstractSound {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ protected static NativeSoundImpl impl;\r
+ static {\r
+ impl = (NativeSoundImpl) GWT.create(NativeSoundImpl.class);\r
+ }\r
+\r
+ public static MimeTypeSupport getMimeTypeSupport(String mimeType) {\r
+ return impl.getMimeTypeSupport(mimeType);\r
+ }\r
+\r
+ private int balance = SoundController.DEFAULT_BALANCE;\r
+\r
+ private Element element;\r
+\r
+ private final String mimeType;\r
+\r
+ private final Element soundControllerElement;\r
+\r
+ private int volume = SoundController.DEFAULT_VOLUME;\r
+\r
+ public NativeSound(String mimeType, String url, boolean streaming, boolean crossOrigin,\r
+ Element soundControllerElement) {\r
+ super(mimeType, url, streaming, crossOrigin);\r
+\r
+ this.soundControllerElement = soundControllerElement;\r
+ this.mimeType = mimeType;\r
+ // TODO: determine whether requests can be cross origin\r
+ impl.preload(soundControllerElement, mimeType, url);\r
+ element = impl.createElement(url);\r
+\r
+ MimeTypeSupport mimeTypeSupport = getMimeTypeSupport(mimeType);\r
+ switch (mimeTypeSupport) {\r
+ case MIME_TYPE_SUPPORT_READY:\r
+ setLoadState(LOAD_STATE_SUPPORTED_MAYBE_READY);\r
+ break;\r
+ case MIME_TYPE_NOT_SUPPORTED:\r
+ setLoadState(LOAD_STATE_NOT_SUPPORTED);\r
+ break;\r
+ case MIME_TYPE_SUPPORT_UNKNOWN:\r
+ setLoadState(LOAD_STATE_SUPPORT_NOT_KNOWN);\r
+ break;\r
+ case MIME_TYPE_SUPPORT_NOT_READY:\r
+ setLoadState(LOAD_STATE_SUPPORTED_NOT_READY);\r
+ break;\r
+ default:\r
+ throw new IllegalArgumentException("unknown MIME type support " + mimeTypeSupport);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public int getBalance() {\r
+ return balance;\r
+ }\r
+\r
+ @Override\r
+ public boolean getLooping() {\r
+ return impl.getLooping(element);\r
+ }\r
+\r
+ @Override\r
+ public SoundType getSoundType() {\r
+ return SoundType.NATIVE;\r
+ }\r
+\r
+ @Override\r
+ public int getVolume() {\r
+ return volume;\r
+ }\r
+\r
+ @Override\r
+ public boolean play() {\r
+ return impl.play(soundControllerElement, element, mimeType);\r
+ }\r
+\r
+ @Override\r
+ public void setBalance(int balance) {\r
+ assert balance >= -100;\r
+ assert balance <= 100;\r
+ this.balance = balance;\r
+ impl.setBalance(element, balance);\r
+ }\r
+\r
+ @Override\r
+ public void setLooping(boolean looping) {\r
+ impl.setLooping(element, looping);\r
+ }\r
+\r
+ @Override\r
+ public void setVolume(int volume) {\r
+ assert volume >= 0;\r
+ assert volume <= 100;\r
+ this.volume = volume;\r
+ impl.setVolume(element, volume);\r
+ }\r
+\r
+ @Override\r
+ public void stop() {\r
+ impl.stop(element);\r
+ }\r
+\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client;\r
+\r
+import com.allen_sauer.gwt.voices.client.handler.FiresSoundEvents;\r
+\r
+/**\r
+ * Common interface for all concrete supported sound implementations.\r
+ */\r
+public interface Sound extends FiresSoundEvents {\r
+ /**\r
+ * Enumeration of sound load states.\r
+ */\r
+ enum LoadState {\r
+ /**\r
+ * Play back of this sound's MIME type is known NOT to be supported. Calling\r
+ * {@link Sound#play()} may in rare occasions still work.\r
+ */\r
+ LOAD_STATE_NOT_SUPPORTED,\r
+\r
+ /**\r
+ * The sound load state is unknown, and cannot be determined. Hope for the best. Calling\r
+ * {@link Sound#play()} may or may not work.\r
+ */\r
+ LOAD_STATE_SUPPORT_NOT_KNOWN,\r
+\r
+ /**\r
+ * Play back of this sound's MIME type is supported and this sound object is ready for immediate\r
+ * play back. This means that the sound file has either been downloaded by the client (when not\r
+ * streaming), or is ready for streaming.\r
+ */\r
+ LOAD_STATE_SUPPORTED_AND_READY,\r
+\r
+ /**\r
+ * Play back of this sound's MIME type is known to be supported, however the client browser is\r
+ * unable to provide load notification events. It cannot be programmatically determined when the\r
+ * client has downloaded the sound. When possible, an attempt will be made to begin downloading\r
+ * the sound file in the background. The load state will NOT change to\r
+ * {@link LoadState#LOAD_STATE_SUPPORTED_AND_READY}, or indeed to any other state.\r
+ */\r
+ LOAD_STATE_SUPPORTED_MAYBE_READY,\r
+\r
+ /**\r
+ * Play back of this sound's MIME type is known to be supported, however the sound file has not\r
+ * yet been loaded. The load state is at some point expected to change to\r
+ * {@link LoadState#LOAD_STATE_SUPPORTED_AND_READY}.\r
+ */\r
+ LOAD_STATE_SUPPORTED_NOT_READY,\r
+\r
+ /**\r
+ * All new sounds start in this load state, after which they will transition at least once to a\r
+ * new load state.\r
+ */\r
+ LOAD_STATE_UNINITIALIZED,\r
+ }\r
+\r
+ /**\r
+ * IANA assigned media type {@value}, from RFC 2045/2046. Typical filename\r
+ * extensions include {@literal .au} and {@literal .snd}.\r
+ */\r
+ String MIME_TYPE_AUDIO_BASIC = "audio/basic";\r
+\r
+ /**\r
+ * IANA assigned media type {@value}, from RFC 4337. Typical filename extension\r
+ * {@literal .mp4}.\r
+ *\r
+ * @deprecated Use a more specific MIME Type which includes an appropriate {@literal codecs} parameter\r
+ */\r
+ @Deprecated\r
+ String MIME_TYPE_AUDIO_MP4 = "audio/mp4";\r
+\r
+ /**\r
+ * MIME Type {@value}, for AAC low complexity MP4 as described in\r
+ * RFC 4281. Typical filename extension {@literal .mp4}.\r
+ */\r
+ String MIME_TYPE_AUDIO_MP4_MP4A_40_2 = "audio/mp4; codecs=mp4a.40.2";\r
+\r
+ /**\r
+ * IANA assigned media type {@value}, from RFC 3003. Typical filename extensions\r
+ * include {@literal .mp1}, {@literal .mp2} and {@literal .mp3}.\r
+ *\r
+ * @deprecated Use a more specific MIME Type which includes an appropriate {@literal codecs} parameter\r
+ */\r
+ @Deprecated\r
+ String MIME_TYPE_AUDIO_MPEG = "audio/mpeg";\r
+\r
+ /**\r
+ * Non-standard MIME type {@value} for MP3 audio.\r
+ * Typical filename extension {@literal .mp3}.\r
+ */\r
+ String MIME_TYPE_AUDIO_MPEG_MP3 = "audio/mpeg; codecs=MP3";\r
+\r
+ /**\r
+ * IANA assigned media type {@value}, from RFC 5334. Typical filename\r
+ * extensions include {@literal .ogg}.\r
+ *\r
+ * @deprecated Use a more specific MIME Type which includes an appropriate {@literal codecs} parameter\r
+ */\r
+ @Deprecated\r
+ String MIME_TYPE_AUDIO_OGG = "audio/ogg";\r
+\r
+ /**\r
+ * IANA assigned media type {@value}, from RFC 5334. Typical filename\r
+ * extensions include {@literal .ogg}.\r
+ */\r
+ String MIME_TYPE_AUDIO_OGG_FLAC = "audio/ogg; codecs=flac";\r
+\r
+ /**\r
+ * IANA assigned media type {@value}, from RFC 5334. Typical filename\r
+ * extensions include {@literal .ogg}.\r
+ */\r
+ String MIME_TYPE_AUDIO_OGG_SPEEX = "audio/ogg; codecs=speex";\r
+\r
+ /**\r
+ * IANA assigned media type {@value}, from RFC 5334. Typical filename\r
+ * extensions include {@literal .ogg}.\r
+ */\r
+ String MIME_TYPE_AUDIO_OGG_VORBIS = "audio/ogg; codecs=vorbis";\r
+\r
+ /**\r
+ * MIME Type {@value} for WAVE audio in Microsoft ADPCM format.\r
+ * See RFC 2361 (WAVE and AVI Codec Registries).\r
+ * Typical filename extension is {@literal .wav}.\r
+ */\r
+ String MIME_TYPE_AUDIO_WAV_ADPCM = "audio/wav; codecs=2";\r
+\r
+ /**\r
+ * MIME Type {@value} for WAVE audio in Microsoft PCM format.\r
+ * See RFC 2361 (WAVE and AVI Codec Registries).\r
+ * Typical filename extension is {@literal .wav}.\r
+ */\r
+ String MIME_TYPE_AUDIO_WAV_PCM = "audio/wav; codecs=1";\r
+\r
+ /**\r
+ * MIME Type {@value} for WAVE audio in unknown format.\r
+ * See RFC 2361 (WAVE and AVI Codec Registries).\r
+ * Typical filename extension is {@literal .wav}.\r
+ */\r
+ String MIME_TYPE_AUDIO_WAV_UNKNOWN = "audio/wav; codecs=0";\r
+\r
+ /**\r
+ * Using {@value} instead of the more popular, but unregistered,\r
+ * {@literal audio/aiff}. Typical filename extension is {@literal .aif}.\r
+ */\r
+ String MIME_TYPE_AUDIO_X_AIFF = "audio/x-aiff";\r
+\r
+ /**\r
+ * Using {@value} instead of the more popular, but unregistered,\r
+ * {@literal audio/midi}. Typical filename extensions include {@literal .mid}. and\r
+ * {@literal .midi}.\r
+ */\r
+ String MIME_TYPE_AUDIO_X_MIDI = "audio/x-midi";\r
+\r
+ /**\r
+ * Using {@value} instead of the more popular, but\r
+ * unregistered, {@literal audio/wav}. Typical filename extension is\r
+ * {@literal .wav}.\r
+ *\r
+ * @deprecated Prefer {@literal audio/wav} over {@literal audio/x-wav}, due\r
+ * to better MAC Safari support. Also use a more specific MIME\r
+ * Type which includes an appropriate {@literal codecs}\r
+ * parameter\r
+ */\r
+ @Deprecated\r
+ String MIME_TYPE_AUDIO_X_WAV = "audio/x-wav";\r
+\r
+ /**\r
+ * Determine this sound's play back balance (range {@literal 1--}..{@literal 100}).\r
+ *\r
+ * @return this sound's current play back balance (range {@literal -100}..{@literal 100})\r
+ */\r
+ int getBalance();\r
+\r
+ /**\r
+ * Determine the current {@link LoadState} of this sound.\r
+ *\r
+ * @return current {@link LoadState}\r
+ */\r
+ LoadState getLoadState();\r
+\r
+ /**\r
+ * Determine whether this sound is to be played in a loop.\r
+ *\r
+ * @return true if this sounds is to be played in a loop\r
+ */\r
+ boolean getLooping();\r
+\r
+ /**\r
+ * Determine this sound's MIME type.\r
+ *\r
+ * @return this sound's MIME type\r
+ */\r
+ String getMimeType();\r
+\r
+ /**\r
+ * Determine the sound type being used for playback.\r
+ *\r
+ * @return the sound type\r
+ */\r
+ SoundType getSoundType();\r
+\r
+ /**\r
+ * Determine the URL for this sound.\r
+ *\r
+ * @return this sound's URL\r
+ */\r
+ String getUrl();\r
+\r
+ /**\r
+ * Determine this soound's play back volume (range {@literal 0}..{@literal 100}).\r
+ *\r
+ * @return this sound's current play back volume (range {@literal 0}..{@literal 100})\r
+ */\r
+ int getVolume();\r
+\r
+ /**\r
+ * Play (or restart) this sound. This methods returns {@literal false} when the sound could not be\r
+ * played. This could happen because the sound system has not yet been initialized, there are no\r
+ * available sound channels, or any number of other reasons. A return value of {@literal true}\r
+ * indicates that the sound probably played. However, this cannot be known with certainty.\r
+ *\r
+ * @return {@literal false} if the sound could not be played, {@literal true} otherwise\r
+ */\r
+ boolean play();\r
+\r
+ /**\r
+ * Set the left/right speaker balance (range {@literal -100}..{@literal 100}).\r
+ *\r
+ * @param balance new balance (range {@literal -100}..{@literal 100})\r
+ */\r
+ void setBalance(int balance);\r
+\r
+ /**\r
+ * Set whether this sound should be played in a loop.\r
+ *\r
+ * @param looping true if the sound is to be looped\r
+ */\r
+ void setLooping(boolean looping);\r
+\r
+ /**\r
+ * Set this sound's volume (range {@literal 0}..{@literal 100}).\r
+ *\r
+ * @param volume new volume (range <{@literal 0}..{@literal 100})\r
+ */\r
+ void setVolume(int volume);\r
+\r
+ /**\r
+ * Stop play back of this sound.\r
+ */\r
+ void stop();\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client;\r
+\r
+import com.google.gwt.core.client.GWT;\r
+import com.google.gwt.dom.client.Style;\r
+import com.google.gwt.dom.client.Style.Overflow;\r
+import com.google.gwt.dom.client.Style.Position;\r
+import com.google.gwt.dom.client.Style.Unit;\r
+import com.google.gwt.user.client.DOM;\r
+import com.google.gwt.user.client.Element;\r
+import com.google.gwt.user.client.Window;\r
+import com.google.gwt.user.client.ui.RootPanel;\r
+\r
+import com.allen_sauer.gwt.voices.client.ui.FlashMovie;\r
+import com.allen_sauer.gwt.voices.client.ui.VoicesMovie;\r
+import com.allen_sauer.gwt.voices.client.util.DOMUtil;\r
+\r
+/**\r
+ * Main class with which client code interact in order to create {@link Sound}\r
+ * objects, which can be played. In addition, each SoundController defines its\r
+ * own default volume and provides the ability to prioritize Flash based sound.\r
+ *\r
+ * <p>\r
+ * For the time being do not create 16 or more SoundControllers as that would\r
+ * result in 16+ Flash Players, which triggers an Adobe bug, mentioned <a\r
+ * href="http://bugzilla.mozilla.org/show_bug.cgi?id=289873#c41">here</a>.\r
+ */\r
+public class SoundController {\r
+ /**\r
+ * Enumeration for varying levels of MIME type support.\r
+ */\r
+ public enum MimeTypeSupport {\r
+ /**\r
+ * Play back of the MIME type is known to NOT be supported in this browser,\r
+ * based on known capabilities of browsers with the same user agent and\r
+ * installed plugins.\r
+ */\r
+ MIME_TYPE_NOT_SUPPORTED,\r
+\r
+ /**\r
+ * Play back of the MIME type is known to be supported in this browser,\r
+ * based on known capabilities of browsers with the same user agent and\r
+ * installed plugins, but this capability has not yet been initialized.\r
+ * Usually this is due to a browser plugin, such as <a href=\r
+ * 'http://www.adobe.com/products/flashplayer/'>Adobe Flash Player\r
+ * < / a > , <a href=\r
+ * 'http://www.apple.com/quicktime/download/'>Apple QuickTime</a> or <a\r
+ * href=\r
+ * 'http://www.microsoft.com/windows/windowsmedia/'>Windows Media Playe\r
+ * r < / a > .\r
+ */\r
+ MIME_TYPE_SUPPORT_NOT_READY,\r
+\r
+ /**\r
+ * Play back of the MIME type is known to be supported in this browser,\r
+ * based on known capabilities of browsers with the same user agent and\r
+ * installed plugins.\r
+ */\r
+ MIME_TYPE_SUPPORT_READY,\r
+\r
+ /**\r
+ * It is unknown (cannot be determined) whether play back of the MIME type\r
+ * is supported in this browser, based on known capabilities of browsers\r
+ * with the same user agent and installed plugins.\r
+ */\r
+ MIME_TYPE_SUPPORT_UNKNOWN,\r
+ }\r
+\r
+ static final int DEFAULT_BALANCE = 0;\r
+ static final boolean DEFAULT_LOOPING = false;\r
+ static final int DEFAULT_VOLUME = 100;\r
+\r
+ static {\r
+ setVersion();\r
+ }\r
+\r
+ private static native void setVersion()\r
+ /*-{\r
+ $wnd.$GWT_VOICES_VERSION = "@GWT_VOICES_VERSION@";\r
+ }-*/;\r
+\r
+ /**\r
+ * Our DOM sound container which is positioned off screen.\r
+ */\r
+ protected final Element soundContainer = DOM.createDiv();\r
+ private int defaultVolume = DEFAULT_VOLUME;\r
+ private SoundType[] preferredSoundTypes;\r
+ private VoicesMovie voicesWrapper;\r
+\r
+ /**\r
+ * We avoid using the usual, module relative {@link GWT#getModuleBaseURL()} to\r
+ * force {@literal gwt-voices.swf} to be loaded from the same domain as the\r
+ * host page. This avoids cross origin issues when the page contains a\r
+ * {@code <base href="http://...">} tag. To override the base URL in your\r
+ * code, use {@link SoundController#setGwtVoicesSwfLocation(String)}.\r
+ */\r
+ private String gwtVoicesSwfBaseUrl = GWT.getHostPageBaseURL() + GWT.getModuleName() + "/";\r
+\r
+ /**\r
+ * Default constructor to be used by client code.\r
+ */\r
+ public SoundController() {\r
+ initSoundContainer();\r
+ }\r
+\r
+ /**\r
+ * Create a new Sound object using the provided MIME type and URL. To enable\r
+ * streaming, or for cross origin content, use {@link #createSound(String, String, boolean, boolean)}.\r
+ *\r
+ * @param mimeType MIME type of the new Sound object\r
+ * @param url location of the new Sound object\r
+ * @return a new Sound object\r
+ */\r
+ public Sound createSound(String mimeType, String url) {\r
+ return createSound(mimeType, url, false, false);\r
+ }\r
+\r
+ /**\r
+ * Create a new Sound object using the provided MIME type and URL.\r
+ *\r
+ * @param mimeType MIME type of the new Sound object\r
+ * @param url location of the new Sound object\r
+ * @param streaming whether or not to allow play back to start before sound\r
+ * has been fully downloaded\r
+ * @param crossOrigin true of the resource is cross origin, false for same origin\r
+ * @return a new Sound object\r
+ */\r
+ public Sound createSound(String mimeType, String url, boolean streaming, boolean crossOrigin) {\r
+ Sound sound = createSoundImpl(mimeType, url, streaming, crossOrigin);\r
+ sound.setVolume(defaultVolume);\r
+ return sound;\r
+ }\r
+\r
+ /**\r
+ * Determine the current preferred sound types for new sounds.\r
+ *\r
+ * @return the current preferred sound types\r
+ *\r
+ * @deprecated this method will be removed in a future release\r
+ */\r
+ @Deprecated\r
+ public SoundType[] getPreferredSoundTypes() {\r
+ return preferredSoundTypes;\r
+ }\r
+\r
+ /**\r
+ * Set the default volume (range <code>0-100</code>) for new sound.\r
+ *\r
+ * @param defaultVolume the default volume (range <code>0-100</code>) to be\r
+ * used for new sounds\r
+ */\r
+ public void setDefaultVolume(int defaultVolume) {\r
+ this.defaultVolume = defaultVolume;\r
+ }\r
+\r
+ /**\r
+ * Set preferred {@link Sound} class: {@link Html5Sound}, {@link FlashSound},\r
+ * {@link NativeSound}.\r
+ *\r
+ * @param soundTypes the preferred sound types\r
+ *\r
+ * @deprecated this method is a temporary stop-gap, may be retired at any\r
+ * time, and may be made to do nothing at all without warning\r
+ */\r
+ @Deprecated\r
+ public void setPreferredSoundTypes(SoundType... soundTypes) {\r
+ for (SoundType s : soundTypes) {\r
+ assert s != null;\r
+ }\r
+ preferredSoundTypes = soundTypes;\r
+ }\r
+\r
+ /**\r
+ * Provides a way to set the base URL for the {@literal gwt-voices.swf} file.\r
+ * The provided base URL must ends with a trailing {@literal /}.\r
+ *\r
+ * @param gwtVoicesSwfBaseUrl base URL relative to which\r
+ * {@literal gwt-voices.swf} can be found\r
+ */\r
+ public void setGwtVoicesSwfLocation(String gwtVoicesSwfBaseUrl) {\r
+ assert gwtVoicesSwfBaseUrl.endsWith("/");\r
+ this.gwtVoicesSwfBaseUrl = gwtVoicesSwfBaseUrl;\r
+ }\r
+\r
+ /**\r
+ * Lazily instantiate Flash Movie so browser plug-in is not unnecessarily\r
+ * triggered.\r
+ *\r
+ * @return the new movie widget\r
+ */\r
+ protected VoicesMovie getVoicesMovie() {\r
+ if (voicesWrapper == null) {\r
+ voicesWrapper = new VoicesMovie(DOMUtil.getUniqueId(), gwtVoicesSwfBaseUrl);\r
+ DOM.appendChild(soundContainer, voicesWrapper.getElement());\r
+ }\r
+ return voicesWrapper;\r
+ }\r
+\r
+ private Sound createSoundImplHtml5(String mimeType, String url, boolean streaming,\r
+ boolean crossOrigin) {\r
+ if (Html5Sound.getMimeTypeSupport(mimeType) == MimeTypeSupport.MIME_TYPE_SUPPORT_READY) {\r
+ return new Html5Sound(mimeType, url, streaming, crossOrigin);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ private Sound createSoundImpl(String mimeType, String url, boolean streaming, boolean crossOrigin) {\r
+ Sound sound = null;\r
+ for (SoundType c : preferredSoundTypes) {\r
+ switch(c) {\r
+ case HTML5:\r
+ sound = createSoundImplHtml5(mimeType, url, streaming, crossOrigin);\r
+ break;\r
+ case FLASH:\r
+ sound = createSoundImplFlash(mimeType, url, streaming, crossOrigin);\r
+ break;\r
+ case NATIVE:\r
+ sound = createSoundImplWebAudio(mimeType, url, streaming, crossOrigin);\r
+ break;\r
+ case WEB_AUDIO:\r
+ sound = createSoundImplWebAudio(mimeType, url, streaming, crossOrigin);\r
+ break;\r
+ }\r
+ if (sound != null) {\r
+ return sound;\r
+ }\r
+ }\r
+\r
+ // Web Audio rocks\r
+ sound = createSoundImplWebAudio(mimeType, url, streaming, crossOrigin);\r
+ if (sound != null) {\r
+ return sound;\r
+ }\r
+\r
+ sound = createSoundImplFlash(mimeType, url, streaming, crossOrigin);\r
+ if (sound != null) {\r
+ return sound;\r
+ }\r
+\r
+ sound = createSoundImplHtml5(mimeType, url, streaming, crossOrigin);\r
+ if (sound != null) {\r
+ return sound;\r
+ }\r
+\r
+ // Fallback to native browser audio\r
+ sound = new NativeSound(mimeType, url, streaming, crossOrigin, soundContainer);\r
+ return sound;\r
+ }\r
+\r
+ private Sound createSoundImplWebAudio(String mimeType, String url, boolean streaming,\r
+ boolean crossOrigin) {\r
+ if (WebAudioSound.getMimeTypeSupport(mimeType) == MimeTypeSupport.MIME_TYPE_SUPPORT_READY) {\r
+ return new WebAudioSound(mimeType, url, streaming, crossOrigin);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ private FlashSound createSoundImplFlash(String mimeType, String url, boolean streaming,\r
+ boolean crossOrigin) {\r
+ if (url.startsWith("data:")) {\r
+ return null;\r
+ }\r
+ if (FlashMovie.isExternalInterfaceSupported()) {\r
+ VoicesMovie vm = getVoicesMovie();\r
+ MimeTypeSupport mimeTypeSupport = vm.getMimeTypeSupport(mimeType);\r
+ if (mimeTypeSupport == MimeTypeSupport.MIME_TYPE_SUPPORT_READY\r
+ || mimeTypeSupport == MimeTypeSupport.MIME_TYPE_SUPPORT_NOT_READY) {\r
+ FlashSound sound = new FlashSound(mimeType, url, streaming, crossOrigin, vm);\r
+ return sound;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ private void initSoundContainer() {\r
+ String gwtVoices = Window.Location.getParameter(SoundType.QUERY_PARAMETER_NAME);\r
+ if (SoundType.FLASH.getQueryParameterValue().equals(gwtVoices)) {\r
+ setPreferredSoundTypes(SoundType.FLASH);\r
+ } else if (SoundType.HTML5.getQueryParameterValue().equals(gwtVoices)) {\r
+ setPreferredSoundTypes(SoundType.HTML5);\r
+ } else if (SoundType.WEB_AUDIO.getQueryParameterValue().equals(gwtVoices)) {\r
+ setPreferredSoundTypes(SoundType.WEB_AUDIO);\r
+ } else if (SoundType.NATIVE.getQueryParameterValue().equals(gwtVoices)) {\r
+ setPreferredSoundTypes(SoundType.NATIVE);\r
+ } else {\r
+ // For now, prefer Flash over HTML5\r
+ setPreferredSoundTypes(SoundType.WEB_AUDIO, SoundType.FLASH, SoundType.HTML5);\r
+ }\r
+\r
+ // place off screen with fixed dimensions and overflow:hidden\r
+ RootPanel.getBodyElement().appendChild(soundContainer);\r
+ Style style = soundContainer.getStyle();\r
+ style.setPosition(Position.ABSOLUTE);\r
+ style.setOverflow(Overflow.HIDDEN);\r
+ style.setLeft(-500, Unit.PX);\r
+ style.setTop(-500, Unit.PX);\r
+ style.setWidth(0, Unit.PX);\r
+ style.setHeight(0, Unit.PX);\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2012 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client;\r
+\r
+/**\r
+ * Sound type implementations.\r
+ */\r
+public enum SoundType {\r
+ /**\r
+ * Flash Audio.\r
+ */\r
+ FLASH("flash"),\r
+\r
+ /**\r
+ * HTML5 Audio.\r
+ */\r
+ HTML5("html5"),\r
+\r
+ /**\r
+ * Native browser audio, e.g. <code><BGSOUND></code> and <code><OBJECT></code>.\r
+ */\r
+ NATIVE("native"),\r
+\r
+ /**\r
+ * Modern Web Audio API.\r
+ */\r
+ WEB_AUDIO("webaudio");\r
+ \r
+ /**\r
+ * Query parameter name, which can be used to request a specific audio implementation\r
+ * at runtime.\r
+ */\r
+ public static final String QUERY_PARAMETER_NAME = "gwt-voices";\r
+\r
+ private final String queryParameterValue;\r
+\r
+ private SoundType(String queryParameterValue) {\r
+ this.queryParameterValue = queryParameterValue;\r
+ }\r
+\r
+ /**\r
+ * Get the query parameter value which may be assigned to the {@literal gwt-voices} URL query\r
+ * parameter.\r
+ * \r
+ * @return value which may be used with the {@literal gwt-voices} URL query parameter\r
+ */\r
+ public String getQueryParameterValue() {\r
+ return queryParameterValue;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client;\r
+\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_NOT_SUPPORTED;\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORTED_NOT_READY;\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_SUPPORT_NOT_KNOWN;\r
+\r
+import com.google.gwt.core.client.JavaScriptObject;\r
+import com.google.gwt.dom.client.Element;\r
+\r
+import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport;\r
+\r
+/**\r
+ * Sound object using the Web Audio API.\r
+ */\r
+public class WebAudioSound extends AbstractSound {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ private String mimeType;\r
+ private Element voice;\r
+ private static Element audioContext;\r
+ private JavaScriptObject buffer;\r
+ private int volume;\r
+ private boolean looping;\r
+\r
+ public static MimeTypeSupport getMimeTypeSupport(String mimeType) {\r
+ if (audioContext == null) {\r
+ return MimeTypeSupport.MIME_TYPE_NOT_SUPPORTED;\r
+ }\r
+ return Html5Sound.getMimeTypeSupport(mimeType);\r
+ }\r
+\r
+ public WebAudioSound(String mimeType, String url, boolean streaming, boolean crossOrigin) {\r
+ super(mimeType, url, streaming, crossOrigin);\r
+\r
+ this.mimeType = mimeType;\r
+\r
+ try {\r
+ createVoice(url, crossOrigin);\r
+ } catch (Throwable e) {\r
+ setLoadState(LOAD_STATE_NOT_SUPPORTED);\r
+ }\r
+\r
+ MimeTypeSupport mimeTypeSupport = getMimeTypeSupport(mimeType);\r
+ switch (mimeTypeSupport) {\r
+ case MIME_TYPE_SUPPORT_READY:\r
+ setLoadState(LOAD_STATE_SUPPORTED_NOT_READY);\r
+ break;\r
+ case MIME_TYPE_NOT_SUPPORTED:\r
+ setLoadState(LOAD_STATE_NOT_SUPPORTED);\r
+ break;\r
+ case MIME_TYPE_SUPPORT_UNKNOWN:\r
+ setLoadState(LOAD_STATE_SUPPORT_NOT_KNOWN);\r
+ break;\r
+ case MIME_TYPE_SUPPORT_NOT_READY:\r
+ setLoadState(LOAD_STATE_SUPPORTED_NOT_READY);\r
+ break;\r
+ default:\r
+ throw new IllegalArgumentException("unknown MIME type support " + mimeTypeSupport);\r
+ }\r
+ }\r
+\r
+ static {\r
+ audioContext = createAudioContext();\r
+ }\r
+\r
+ private static native Element createAudioContext() /*-{\r
+ try {\r
+ return new AudioContext();\r
+ } catch (ignore) {\r
+ }\r
+\r
+ try {\r
+ return new webkitAudioContext();\r
+ } catch (ignore) {\r
+ }\r
+\r
+ return null;\r
+ }-*/;\r
+\r
+ private native void createVoice(String url, boolean crossOrigin) /*-{\r
+ var context = @com.allen_sauer.gwt.voices.client.WebAudioSound::audioContext;\r
+\r
+ // TODO: Implement XDomainRequest for IE\r
+ var request = new $wnd.XMLHttpRequest();\r
+ request.open("GET", url, true);\r
+ request.responseType = "arraybuffer";\r
+\r
+ var self = this;\r
+ request.onload = function() {\r
+ try {\r
+ self.@com.allen_sauer.gwt.voices.client.WebAudioSound::buffer = context\r
+ .createBuffer(request.response, false);\r
+ self.@com.allen_sauer.gwt.voices.client.WebAudioSound::soundLoaded()();\r
+ } catch(e) {\r
+ self.@com.allen_sauer.gwt.voices.client.WebAudioSound::soundLoadFailed()();\r
+ }\r
+ }\r
+\r
+ request.send();\r
+ }-*/;\r
+\r
+ private void soundLoadFailed() {\r
+ setLoadState(LoadState.LOAD_STATE_NOT_SUPPORTED);\r
+ }\r
+\r
+ private void soundLoaded() {\r
+ setLoadState(LoadState.LOAD_STATE_SUPPORTED_AND_READY);\r
+ }\r
+ \r
+ @Override\r
+ public int getBalance() {\r
+ // TODO(fredsa): Auto-generated method stub\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ public boolean getLooping() {\r
+ return looping;\r
+ }\r
+\r
+ @Override\r
+ public int getVolume() {\r
+ return volume;\r
+ }\r
+\r
+ @Override\r
+ public native boolean play() /*-{\r
+ var buffer = this.@com.allen_sauer.gwt.voices.client.WebAudioSound::buffer;\r
+\r
+ if (buffer == null) {\r
+ // XHR has not yet returned\r
+ return false;\r
+ }\r
+ this.@com.allen_sauer.gwt.voices.client.WebAudioSound::stop()();\r
+ var context = @com.allen_sauer.gwt.voices.client.WebAudioSound::audioContext;\r
+\r
+ var voice = context.createBufferSource();\r
+ this.@com.allen_sauer.gwt.voices.client.WebAudioSound::voice = voice;\r
+\r
+ if (this.@com.allen_sauer.gwt.voices.client.WebAudioSound::looping) {\r
+ voice.loop = true;\r
+ }\r
+\r
+ var node = voice;\r
+\r
+ var volume = this.@com.allen_sauer.gwt.voices.client.WebAudioSound::volume;\r
+ if (volume != @com.allen_sauer.gwt.voices.client.SoundController::DEFAULT_VOLUME) {\r
+ var gainNode = context.createGainNode();\r
+ gainNode.gain.value = volume\r
+ / @com.allen_sauer.gwt.voices.client.SoundController::DEFAULT_VOLUME;\r
+ node.connect(gainNode);\r
+ node = gainNode;\r
+ }\r
+\r
+ node.connect(context.destination);\r
+\r
+ voice.buffer = buffer;\r
+\r
+ voice.noteOn(context.currentTime);\r
+\r
+ // TODO Replace setTimeout() once https://bugs.webkit.org/show_bug.cgi?id=71942 is fixed\r
+ if (!voice.loop) {\r
+ var self = this;\r
+ setTimeout(\r
+ function() {\r
+ self.@com.allen_sauer.gwt.voices.client.WebAudioSound::plackbackCompleted()();\r
+ }, voice.buffer.duration * 1000);\r
+ }\r
+\r
+ return true;\r
+ }-*/;\r
+\r
+ private void plackbackCompleted() {\r
+ soundHandlerCollection.fireOnPlaybackComplete(this);\r
+ }\r
+ \r
+ @Override\r
+ public void setBalance(int balance) {\r
+ // TODO(fredsa): Auto-generated method stub\r
+ }\r
+\r
+ @Override\r
+ public void setLooping(boolean looping) {\r
+ this.looping = looping;\r
+ }\r
+\r
+ @Override\r
+ public void setVolume(int volume) {\r
+ this.volume = volume;\r
+ }\r
+\r
+ @Override\r
+ public native void stop() /*-{\r
+ var context = @com.allen_sauer.gwt.voices.client.WebAudioSound::audioContext;\r
+ var voice = this.@com.allen_sauer.gwt.voices.client.WebAudioSound::voice;\r
+ if (voice == null) {\r
+ return;\r
+ }\r
+ voice.noteOff(context.currentTime);\r
+ this.@com.allen_sauer.gwt.voices.client.WebAudioSound::voice = null;\r
+ }-*/;\r
+\r
+ @Override\r
+ public SoundType getSoundType() {\r
+ return SoundType.WEB_AUDIO;\r
+ }\r
+\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.handler;\r
+\r
+/**\r
+ * Interface for classes wishing to fire sound events.\r
+ */\r
+public interface FiresSoundEvents {\r
+ /**\r
+ * Register a new sound handler.\r
+ * \r
+ * @param handler sound handler to register\r
+ */\r
+ void addEventHandler(SoundHandler handler);\r
+\r
+ /**\r
+ * Remove a previously registered sound handler.\r
+ * \r
+ * @param handler sound handler to unregister\r
+ */\r
+ void removeEventHandler(SoundHandler handler);\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.handler;\r
+\r
+import com.allen_sauer.gwt.voices.client.Sound;\r
+import com.allen_sauer.gwt.voices.client.util.StringUtil;\r
+\r
+import java.util.EventObject;\r
+\r
+/**\r
+ * Event object when play back of a sound completes.\r
+ */\r
+@SuppressWarnings("serial")\r
+public class PlaybackCompleteEvent extends EventObject {\r
+ /**\r
+ * Constructor, used by {@link SoundHandlerCollection}.\r
+ * \r
+ * @param source the {@link Sound} object which completed play back\r
+ */\r
+ public PlaybackCompleteEvent(Object source) {\r
+ super(source);\r
+ }\r
+\r
+ /**\r
+ * Returns the string <code>"PlaybackCompleteEvent: "</code> followed by\r
+ * <code>sound.toString()</code>, whereby <code>sound</code> is the <code>source</code> of this\r
+ * {@link EventObject}.\r
+ * \r
+ * @return the string representation of this event\r
+ */\r
+ @Override\r
+ public String toString() {\r
+ Sound sound = (Sound) getSource();\r
+ return StringUtil.getSimpleName(PlaybackCompleteEvent.class) + ": " + sound;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.handler;\r
+\r
+import java.util.EventListener;\r
+\r
+/**\r
+ * Implement this interface to receive events related to sound load states sound playback.\r
+ */\r
+public interface SoundHandler extends EventListener {\r
+ /**\r
+ * Fired when playback of a sound completes.\r
+ * \r
+ * @param event the completion event\r
+ */\r
+ void onPlaybackComplete(PlaybackCompleteEvent event);\r
+\r
+ /**\r
+ * Fired when the load state changes.\r
+ * \r
+ * @param event the load state completion event\r
+ */\r
+ void onSoundLoadStateChange(SoundLoadStateChangeEvent event);\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.handler;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * Utility collection class used by AbstractSound.\r
+ */\r
+@SuppressWarnings("serial")\r
+public class SoundHandlerCollection extends ArrayList<SoundHandler> {\r
+ /**\r
+ * Fires when playback of a sound completes.\r
+ * \r
+ * @param sender the sound which finished playing\r
+ */\r
+ public void fireOnPlaybackComplete(Object sender) {\r
+ PlaybackCompleteEvent event = new PlaybackCompleteEvent(sender);\r
+\r
+ for (SoundHandler handler : this) {\r
+ handler.onPlaybackComplete(event);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Fires when a sound's load state changes.\r
+ * \r
+ * @param sender the sound who's load state changed\r
+ */\r
+ public void fireOnSoundLoadStateChange(Object sender) {\r
+ SoundLoadStateChangeEvent event = new SoundLoadStateChangeEvent(sender);\r
+\r
+ for (SoundHandler handler : this) {\r
+ handler.onSoundLoadStateChange(event);\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.handler;\r
+\r
+import com.allen_sauer.gwt.voices.client.Sound;\r
+import com.allen_sauer.gwt.voices.client.Sound.LoadState;\r
+import com.allen_sauer.gwt.voices.client.util.StringUtil;\r
+\r
+import java.util.EventObject;\r
+\r
+/**\r
+ * Event object representing a load state change.\r
+ */\r
+@SuppressWarnings("serial")\r
+public class SoundLoadStateChangeEvent extends EventObject {\r
+ private final LoadState loadState;\r
+\r
+ /**\r
+ * Event constructor.\r
+ * \r
+ * @param source the sound object\r
+ */\r
+ public SoundLoadStateChangeEvent(Object source) {\r
+ super(source);\r
+ Sound sound = (Sound) source;\r
+ loadState = sound.getLoadState();\r
+ }\r
+\r
+ /**\r
+ * Determine the load state for this event.\r
+ * \r
+ * @return this event's load state\r
+ */\r
+ public LoadState getLoadState() {\r
+ return loadState;\r
+ }\r
+\r
+ /**\r
+ * Get a string representation of this event object.\r
+ * \r
+ * @return a string representation of this event object\r
+ */\r
+ @Override\r
+ public String toString() {\r
+ Sound sound = (Sound) getSource();\r
+ return StringUtil.getSimpleName(SoundLoadStateChangeEvent.class) + ": " + sound + "; "\r
+ + loadState.name();\r
+ }\r
+}\r
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\r
+</head>\r
+<body>\r
+This package contains sound event handler related classes.\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\r
+</head>\r
+<body>\r
+This package contains the main classes used in client application code.\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui;\r
+\r
+import com.google.gwt.core.client.GWT;\r
+import com.google.gwt.user.client.Element;\r
+\r
+import com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImpl;\r
+\r
+/**\r
+ * Flash movie.\r
+ */\r
+public class FlashMovie {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ /**\r
+ * Flash movie MIME type (<code>{@value}</code>). Typical filename extension is <code>.swf</code>.\r
+ */\r
+ public static final String MIME_TYPE_APPLICATION_X_SHOCKWAVE_FLASH = "application/x-shockwave-flash";\r
+\r
+ private static FlashMovieImpl impl;\r
+\r
+ /**\r
+ * We require at least Flash version ({@value}), since it provides 32 audio channels. We\r
+ * previously required Flash version 8, which provides support for <code>ExternalInterface</code>.\r
+ */\r
+ private static final int MINIMUM_MAJOR_VERSION = 9;\r
+\r
+ static {\r
+ impl = (FlashMovieImpl) GWT.create(FlashMovieImpl.class);\r
+ }\r
+\r
+ public static int getMajorVersion() {\r
+ return impl.getMajorVersion();\r
+ }\r
+\r
+ public static boolean isExternalInterfaceSupported() {\r
+ return getMajorVersion() >= MINIMUM_MAJOR_VERSION;\r
+ }\r
+\r
+ private Element element;\r
+\r
+ public FlashMovie(String id, String url) {\r
+ element = impl.createElementMaybeSetURL(id, url);\r
+ }\r
+\r
+ public Element getElement() {\r
+ return element;\r
+ }\r
+\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui;\r
+\r
+import static com.allen_sauer.gwt.voices.client.Sound.LoadState.LOAD_STATE_NOT_SUPPORTED;\r
+import static com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport.MIME_TYPE_NOT_SUPPORTED;\r
+import static com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport.MIME_TYPE_SUPPORT_NOT_READY;\r
+import static com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport.MIME_TYPE_SUPPORT_READY;\r
+import static com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport.MIME_TYPE_SUPPORT_UNKNOWN;\r
+\r
+import com.google.gwt.core.client.GWT;\r
+import com.google.gwt.core.client.Scheduler;\r
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;\r
+\r
+import com.allen_sauer.gwt.voices.client.FlashSound;\r
+import com.allen_sauer.gwt.voices.client.Sound;\r
+import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport;\r
+import com.allen_sauer.gwt.voices.client.util.StringUtil;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+\r
+// CHECKSTYLE_JAVADOC_OFF\r
+public class VoicesMovie extends FlashMovie {\r
+\r
+ @SuppressWarnings("deprecation")\r
+ private static final String[] FLASH_SUPPORTED_MIME_TYPES = {\r
+ Sound.MIME_TYPE_AUDIO_MPEG, Sound.MIME_TYPE_AUDIO_MPEG_MP3,};\r
+ private static final String GWT_VOICES_SWF = "gwt-voices.swf";\r
+\r
+\r
+ private MimeTypeSupport flashSupport = MIME_TYPE_SUPPORT_UNKNOWN;\r
+ private final ArrayList<FlashSound> unitializedSoundList = new ArrayList<FlashSound>();\r
+\r
+ public VoicesMovie(String id, String gwtVoicesSwfBaseUrl) {\r
+ super(id, gwtVoicesSwfBaseUrl + GWT_VOICES_SWF);\r
+ installFlashCallbackHooks(id);\r
+\r
+ // Flash Player version check for ExternalInterface support\r
+ if (isExternalInterfaceSupported()) {\r
+ flashSupport = MIME_TYPE_SUPPORT_NOT_READY;\r
+ } else {\r
+ flashSupport = MIME_TYPE_NOT_SUPPORTED;\r
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {\r
+ @Override\r
+ public void execute() {\r
+ movieUnsupported();\r
+ }\r
+ });\r
+ }\r
+ }\r
+\r
+ public MimeTypeSupport getMimeTypeSupport(String mimeType) {\r
+ switch (flashSupport) {\r
+ case MIME_TYPE_SUPPORT_READY:\r
+ case MIME_TYPE_SUPPORT_NOT_READY:\r
+ return StringUtil.contains(FLASH_SUPPORTED_MIME_TYPES, mimeType) ? MIME_TYPE_SUPPORT_READY\r
+ : MIME_TYPE_NOT_SUPPORTED;\r
+ case MIME_TYPE_SUPPORT_UNKNOWN:\r
+ case MIME_TYPE_NOT_SUPPORTED:\r
+ return flashSupport;\r
+ default:\r
+ throw new RuntimeException("Unhandled flash support value " + flashSupport);\r
+ }\r
+ }\r
+\r
+ public boolean playSound(int id) {\r
+ if (flashSupport == MIME_TYPE_SUPPORT_READY) {\r
+ return callPlaySound(id);\r
+ }\r
+ // the sound was not played, return false\r
+ return false;\r
+ }\r
+\r
+ public void registerSound(FlashSound flashSound) {\r
+ if (flashSupport == MIME_TYPE_SUPPORT_READY) {\r
+ doCreateSound(flashSound);\r
+ } else {\r
+ unitializedSoundList.add(flashSound);\r
+ }\r
+ }\r
+\r
+ public void setBalance(int id, int balance) {\r
+ assert balance >= -100;\r
+ assert balance <= 100;\r
+ if (flashSupport == MIME_TYPE_SUPPORT_READY) {\r
+ callSetPanning(id, balance / 100f);\r
+ }\r
+ }\r
+\r
+ public void setLooping(int id, boolean looping) {\r
+ if (flashSupport == MIME_TYPE_SUPPORT_READY) {\r
+ callSetLooping(id, looping ? Integer.MAX_VALUE : 0);\r
+ }\r
+ }\r
+\r
+ public void setVolume(int id, int volume) {\r
+ assert volume >= 0;\r
+ assert volume <= 100;\r
+ if (flashSupport == MIME_TYPE_SUPPORT_READY) {\r
+ callSetVolume(id, volume / 100f);\r
+ }\r
+ }\r
+\r
+ public void stopSound(int id) {\r
+ if (flashSupport == MIME_TYPE_SUPPORT_READY) {\r
+ callStopSound(id);\r
+ }\r
+ }\r
+\r
+ protected void debug(String text) {\r
+ System.out.println(text);\r
+ if (!GWT.isProdMode()) {\r
+ consoleDebug(text);\r
+ }\r
+ }\r
+\r
+ private native void callCreateSound(int id, String soundURL, boolean checkPolicyFile) /*-{\r
+ var elem = this.@com.allen_sauer.gwt.voices.client.ui.FlashMovie::element;\r
+ elem.createSound(id, soundURL, checkPolicyFile);\r
+ }-*/;\r
+\r
+ private native boolean callPlaySound(int id) /*-{\r
+ var elem = this.@com.allen_sauer.gwt.voices.client.ui.FlashMovie::element;\r
+ return elem.playSound(id);\r
+ }-*/;\r
+\r
+ private native void callSetLooping(int id, int looping) /*-{\r
+ var elem = this.@com.allen_sauer.gwt.voices.client.ui.FlashMovie::element;\r
+ elem.setLooping(id, looping);\r
+ }-*/;\r
+\r
+ private native void callSetPanning(int id, float panning) /*-{\r
+ var elem = this.@com.allen_sauer.gwt.voices.client.ui.FlashMovie::element;\r
+ elem.setPanning(id, panning);\r
+ }-*/;\r
+\r
+ private native void callSetVolume(int id, float volume) /*-{\r
+ var elem = this.@com.allen_sauer.gwt.voices.client.ui.FlashMovie::element;\r
+ elem.setVolume(id, volume);\r
+ }-*/;\r
+\r
+ private native void callStopSound(int id) /*-{\r
+ var elem = this.@com.allen_sauer.gwt.voices.client.ui.FlashMovie::element;\r
+ elem.stopSound(id);\r
+ }-*/;\r
+\r
+ private native void consoleDebug(String text) /*-{\r
+ console.log(text);\r
+ }-*/;\r
+\r
+ private void doCreateSound(FlashSound flashSound) {\r
+ callCreateSound(flashSound.getSoundNumber(), flashSound.getUrl(), flashSound.isCrossOrigin());\r
+ }\r
+\r
+ private native void installFlashCallbackHooks(String id) /*-{\r
+ if ($doc.VoicesMovie === undefined) {\r
+ $doc.VoicesMovie = {};\r
+ }\r
+ var self = this;\r
+ $doc.VoicesMovie[id] = {};\r
+\r
+ $doc.VoicesMovie[id].ready = function() {\r
+ self.@com.allen_sauer.gwt.voices.client.ui.VoicesMovie::movieReady()();\r
+ }\r
+\r
+ $doc.VoicesMovie[id].soundLoaded = function(id) {\r
+ @com.allen_sauer.gwt.voices.client.FlashSound::soundLoaded(I)(id);\r
+ return true;\r
+ }\r
+\r
+ $doc.VoicesMovie[id].playbackCompleted = function(id) {\r
+ @com.allen_sauer.gwt.voices.client.FlashSound::playbackCompleted(I)(id);\r
+ }\r
+\r
+ $doc.VoicesMovie[id].log = function(text) {\r
+ self.@com.allen_sauer.gwt.voices.client.ui.VoicesMovie::debug(Ljava/lang/String;)("FLASH[" + id + "]: " + text);\r
+ }\r
+ }-*/;\r
+\r
+ private void movieReady() {\r
+ flashSupport = MIME_TYPE_SUPPORT_READY;\r
+ for (Iterator<FlashSound> iterator = unitializedSoundList.iterator(); iterator.hasNext();) {\r
+ FlashSound flashSound = iterator.next();\r
+ doCreateSound(flashSound);\r
+ iterator.remove();\r
+ }\r
+ }\r
+\r
+ private void movieUnsupported() {\r
+ for (FlashSound flashSound : unitializedSoundList) {\r
+ flashSound.setLoadState(LOAD_STATE_NOT_SUPPORTED);\r
+ // Flash plug-in may become available later; do not call iterator.remove()\r
+ }\r
+ }\r
+\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+import com.google.gwt.user.client.Element;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.ui.FlashMovie} default cross-browser\r
+ * implementation.\r
+ */\r
+public abstract class FlashMovieImpl {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ public abstract Element createElementMaybeSetURL(String id, String url);\r
+\r
+ /**\r
+ * Returns a major version number, starting with Flash Players version 3 or 4, depending on the\r
+ * browser. Earlier versions are not currently detected and result in <code>0</code> being\r
+ * returned.\r
+ * \r
+ * @return major version number for the installed Flash Player, or <code>0</code> if version\r
+ * cannot be determined, or Flash Player is not installed\r
+ */\r
+ public int getMajorVersion() {\r
+ String versionString = getVersionString();\r
+ return versionString == null ? 0 : Integer.parseInt(versionString.replaceFirst(",.*", ""));\r
+ }\r
+\r
+ /**\r
+ * Returns a generic string of comma delimited version numbers, e.g. <code>9,0,47,0</code> or\r
+ * <code>9,0,47</code>, starting with Flash Players version 3 or 4, depending on the browser.\r
+ * Earlier versions are not currently detected and result in <code>null</code> being returned.\r
+ * \r
+ * @return generic version string or <code>null</code> if version could not be determined, or\r
+ * plug-in is unavailable\r
+ */\r
+ public String getVersionString() {\r
+ String rawVersionString = getRawVersionString();\r
+ return rawVersionString == null ? null\r
+ : rawVersionString.replaceAll("\\D*(\\d+)", "$1,").replaceFirst(",$", "");\r
+ }\r
+\r
+ /**\r
+ * Returns a browser specific version string such as <code>WIN 9,0,47,0</code> or\r
+ * <code>Shockwave Flash 9.0 r47</code>, starting with Flash Player version\r
+ * 3. Earlier versions are not currently detected and result in <code>null</code> being returned.\r
+ * \r
+ * @return non-IE Flash plug-in version string or <code>null</code> if version could not be\r
+ * determined, or plug-in is unavailable\r
+ */\r
+ protected abstract String getRawVersionString();\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+import com.google.gwt.user.client.Element;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.ui.FlashMovie} implementation for IE.\r
+ */\r
+public class FlashMovieImplIE6 extends FlashMovieImpl {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ @Override\r
+ public native Element createElementMaybeSetURL(String id, String url)\r
+ /*-{\r
+ var elem = $doc.createElement("object");\r
+ elem.tabIndex = -1;\r
+ elem.id = id;\r
+ elem.classid = "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000";\r
+ elem.codebase = "http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0";\r
+ // elem.Quality = 1; // 0=Low, 1=High, 2=AutoLow, 3=AutoHigh\r
+ // elem.ScaleMode = 2; //0=ShowAll, 1=NoBorder, 2=ExactFit\r
+\r
+ // Must be set after the classid\r
+ elem.FlashVars = "id=" + id;\r
+ elem.Movie = url;\r
+ return elem;\r
+ }-*/;\r
+\r
+ /**\r
+ * Returns an IE specific version string such as {@literal WIN 9,0,47,0} or\r
+ * {@literal UNIX 9,0,47,0}, starting with Flash Player version 4,0,11,0. Earlier versions\r
+ * are not currently detected and result in <code>null</code> being returned.\r
+ *\r
+ * @return IE specific Flash plug-in version string or <code>null</code> if version could not be\r
+ * determined, or plug-in is unavailable\r
+ */\r
+ @Override\r
+ protected native String getRawVersionString()\r
+ /*-{\r
+ try {\r
+ return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version");\r
+ } catch (e) {\r
+ return null;\r
+ }\r
+ }-*/;\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.ui.FlashMovie} implementation for Mozilla.\r
+ */\r
+public class FlashMovieImplMozilla extends FlashMovieImplStandard {\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.ui.FlashMovie} implementation for older Mozillae.\r
+ */\r
+public class FlashMovieImplMozillaOld extends FlashMovieImplMozilla {\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.ui.FlashMovie} implementation for Opera.\r
+ */\r
+public class FlashMovieImplOpera extends FlashMovieImplStandard {\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.ui.FlashMovie} implementation for Webkit/Safari.\r
+ */\r
+public class FlashMovieImplSafari extends FlashMovieImplStandard {\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+import com.google.gwt.user.client.Element;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.ui.FlashMovie} implementation for standard\r
+ * browsers.\r
+ */\r
+public abstract class FlashMovieImplStandard extends FlashMovieImpl {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ @Override\r
+ public native Element createElementMaybeSetURL(String id, String url)\r
+ /*-{\r
+ var elem = $doc.createElement("object");\r
+ elem.setAttribute("id", id);\r
+ elem.setAttribute("type", "application/x-shockwave-flash");\r
+ elem.setAttribute("data", url);\r
+\r
+ var param = $doc.createElement("param");\r
+ param.setAttribute("name", "FlashVars");\r
+ param.setAttribute("value", "id=" + id);\r
+ elem.appendChild(param);\r
+\r
+ return elem;\r
+ }-*/;\r
+\r
+ @Override\r
+ protected native String getRawVersionString()\r
+ /*-{\r
+ var p = navigator.plugins["Shockwave Flash"];\r
+ return p == null ? null : p.description;\r
+ }-*/;\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+import com.google.gwt.dom.client.Element;\r
+\r
+import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.NativeSound} default cross-browser\r
+ * implementation.\r
+ */\r
+public abstract class NativeSoundImpl {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ public abstract Element createElement(String url);\r
+\r
+ public boolean getLooping(Element element) {\r
+ return element.hasAttribute("loop");\r
+ }\r
+\r
+ public abstract MimeTypeSupport getMimeTypeSupport(String mimeType);\r
+\r
+ public native boolean play(Element soundControllerElement, Element elem, String mimeType)\r
+ /*-{\r
+ soundControllerElement.appendChild(elem);\r
+ // best guess is that the sound played, so return true\r
+ return true;\r
+ }-*/;\r
+\r
+ public void preload(Element soundControllerElement, String mimeType, String url) {\r
+ if (mimeTypeSupportsVolume(mimeType)) {\r
+ Element elem = createElement(url);\r
+ setVolume(elem, 0);\r
+ play(soundControllerElement, elem, mimeType);\r
+ }\r
+ }\r
+\r
+ public abstract void setBalance(Element elem, int balancePercentage);\r
+\r
+ public void setLooping(Element element, boolean looping) {\r
+ element.setAttribute("loop", "infinite");\r
+ }\r
+\r
+ public abstract void setVolume(Element elem, int volume);\r
+\r
+ public native void stop(Element elem)\r
+ /*-{\r
+ var parent = elem.parentNode;\r
+ if (parent != null) {\r
+ parent.removeChild(elem);\r
+ }\r
+ }-*/;\r
+\r
+ protected boolean mimeTypeSupportsVolume(String mimeType) {\r
+ return true;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+import static com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport.MIME_TYPE_NOT_SUPPORTED;\r
+import static com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport.MIME_TYPE_SUPPORT_READY;\r
+\r
+import com.google.gwt.dom.client.Element;\r
+\r
+import com.allen_sauer.gwt.voices.client.Sound;\r
+import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport;\r
+import com.allen_sauer.gwt.voices.client.util.StringUtil;\r
+\r
+import java.util.HashMap;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.NativeSound} implementation for IE.\r
+ */\r
+public class NativeSoundImplIE6 extends NativeSoundImpl {\r
+ /**\r
+ * List based on <a href='http://support.microsoft.com/kb/297477'>How to apply a background sound\r
+ * to a Web page in FrontPage</a> knowledge base article.\r
+ */\r
+ @SuppressWarnings("deprecation")\r
+ private static final String[] BGSOUND_SUPPORTED_MIME_TYPES = {\r
+ Sound.MIME_TYPE_AUDIO_X_AIFF, Sound.MIME_TYPE_AUDIO_BASIC, Sound.MIME_TYPE_AUDIO_X_MIDI,\r
+ Sound.MIME_TYPE_AUDIO_MPEG, Sound.MIME_TYPE_AUDIO_X_WAV,};\r
+\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+ private static final String[] MIME_TYPES_BGSOUND_NO_VOLUME_CONTROL = {Sound.MIME_TYPE_AUDIO_X_MIDI,};\r
+\r
+ private static final String[] MIME_TYPES_ONE_AT_ATIME = {Sound.MIME_TYPE_AUDIO_X_MIDI,};\r
+\r
+ private static final HashMap<String, Element> oneAtATimeElements = new HashMap<String, Element>();\r
+\r
+ @Override\r
+ public native Element createElement(String url)\r
+ /*-{\r
+ var elem = $doc.createElement("bgsound");\r
+ elem.src = url;\r
+ // elem.loop = 1; // -1 = infinitely, 0 = one time, n = number of times\r
+ return elem;\r
+ }-*/;\r
+\r
+ @Override\r
+ public MimeTypeSupport getMimeTypeSupport(String mimeType) {\r
+ return StringUtil.contains(BGSOUND_SUPPORTED_MIME_TYPES, mimeType) ? MIME_TYPE_SUPPORT_READY\r
+ : MIME_TYPE_NOT_SUPPORTED;\r
+ }\r
+\r
+ @Override\r
+ public boolean play(Element soundControllerElement, Element elem, String mimeType) {\r
+ Element currentElement = oneAtATimeElements.remove(mimeType);\r
+ if (currentElement != null) {\r
+ stop(currentElement);\r
+ }\r
+ if (StringUtil.contains(MIME_TYPES_ONE_AT_ATIME, mimeType)) {\r
+ oneAtATimeElements.put(mimeType, elem);\r
+ }\r
+ return super.play(soundControllerElement, elem, mimeType);\r
+ }\r
+\r
+ @Override\r
+ public void preload(Element soundControllerElement, String mimeType, String url) {\r
+ if (!StringUtil.contains(MIME_TYPES_BGSOUND_NO_VOLUME_CONTROL, mimeType)) {\r
+ super.preload(soundControllerElement, mimeType, url);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Best guess at conversion formula from standard -100 .. 100 range to -10000 .. 10000 range used\r
+ * by IE.\r
+ *\r
+ * TODO location documentation for IE\r
+ */\r
+ @Override\r
+ public native void setBalance(Element elem, int balance)\r
+ /*-{\r
+ if (balance == -100) {\r
+ balance = -10000;\r
+ } else if (balance == 100) {\r
+ balance = 10000;\r
+ } else if (balance < 0) {\r
+ balance = 100 - 10000 / (100 + balance);\r
+ } else {\r
+ balance = 10000 / (100 - balance) - 100;\r
+ }\r
+ elem.balance = "" + balance; // -10000 .. 10000\r
+ }-*/;\r
+\r
+ /**\r
+ * Best guess at conversion formula from standard 0 .. 100 range to -10000 .. 0 range used by IE.\r
+ *\r
+ * TODO location documentation for IE\r
+ */\r
+ @Override\r
+ public native void setVolume(Element elem, int volume)\r
+ /*-{\r
+ elem.volume = volume == 0 ? -10000 : (-10000 / volume); // -10000 .. 0\r
+ }-*/;\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.NativeSound} implementation for Mozilla.\r
+ */\r
+public class NativeSoundImplMozilla extends NativeSoundImplStandard {\r
+\r
+ /**\r
+ * Determine whether volume control is supported for the provided MIME type.\r
+ *\r
+ * Returns <code>false</code> if <code>navigator.mimeTypes[mimeType].enabledPlugin.filename</code>\r
+ * ends with <code>wmp.so</code>, indicating the mplayer plugin on Linux, which does not support\r
+ * volume control. Known strings to date are <code>gecko-mediaplayer-wmp.so</code> and\r
+ * <code>mplayerplug-in-wmp.so</code>.\r
+ *\r
+ * @param mimeType the MIME type to test\r
+ * @return true if the MIME type and enabled plugin is believed to provide volume support\r
+ */\r
+ @Override\r
+ protected native boolean mimeTypeSupportsVolume(String mimeType)\r
+ /*-{\r
+ if (mimeType = @com.allen_sauer.gwt.voices.client.Sound::MIME_TYPE_AUDIO_X_MIDI) {\r
+ // No MIDI volume support in Firefox 3.6\r
+ // (Tested on OSX)\r
+ return false;\r
+ }\r
+ var m = navigator.mimeTypes[mimeType];\r
+ if (m != null && m.enabledPlugin != null && m.enabledPlugin.filename.search(/wmp.so$/) != -1) {\r
+ return false;\r
+ }\r
+ return true;\r
+ }-*/;\r
+\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.NativeSound} implementation for older Mozillae.\r
+ */\r
+public class NativeSoundImplMozillaOld extends NativeSoundImplMozilla {\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+import static com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport.MIME_TYPE_SUPPORT_READY;\r
+\r
+import com.google.gwt.dom.client.Element;\r
+\r
+import com.allen_sauer.gwt.voices.client.Sound;\r
+import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport;\r
+import com.allen_sauer.gwt.voices.client.util.StringUtil;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.NativeSound} implementation for Opera.\r
+ */\r
+public class NativeSoundImplOpera extends NativeSoundImplStandard {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ @SuppressWarnings("deprecation")\r
+ private static final String[] SUPPORTED_WITHOUT_PLUGINS_MIME_TYPES = {Sound.MIME_TYPE_AUDIO_X_WAV,};\r
+\r
+ @Override\r
+ public MimeTypeSupport getMimeTypeSupport(String mimeType) {\r
+ if (StringUtil.contains(SUPPORTED_WITHOUT_PLUGINS_MIME_TYPES, mimeType)) {\r
+ return MIME_TYPE_SUPPORT_READY;\r
+ }\r
+ return operaGetMimeTypeSupport(mimeType);\r
+ }\r
+\r
+ @Override\r
+ public native void preload(Element soundControllerElement, String mimeType, String url) /*-{\r
+ new Audio(url);\r
+ }-*/;\r
+\r
+ private native MimeTypeSupport operaGetMimeTypeSupport(String mimeType) /*-{\r
+ var m = navigator.mimeTypes[mimeType];\r
+ // Note, m != null occurs in many browsers for well known MIME types\r
+ // even though the MIME type is not supported without a plug-in\r
+ return (m != null && m.description && m.description.length > 0)\r
+ ? @com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport::MIME_TYPE_SUPPORT_READY\r
+ : @com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport::MIME_TYPE_NOT_SUPPORTED;\r
+ }-*/;\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+import com.google.gwt.dom.client.Element;\r
+\r
+import com.allen_sauer.gwt.voices.client.Sound;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.NativeSound} implementation for Webkit/Safari.\r
+ */\r
+public class NativeSoundImplSafari extends NativeSoundImplStandard {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ @Override\r
+ public native boolean play(Element soundControllerElement, Element elem, String mimeType)\r
+ /*-{\r
+ var parent = elem.parentNode;\r
+ if (parent != null) {\r
+ parent.removeChild(elem);\r
+ }\r
+ soundControllerElement.appendChild(elem);\r
+ // best guess is that the sound played, so return true\r
+ return true;\r
+ }-*/;\r
+\r
+ @Override\r
+ protected boolean mimeTypeSupportsVolume(String mimeType) {\r
+ if (Sound.MIME_TYPE_AUDIO_X_MIDI.equals(mimeType)) {\r
+ // No MIDI volume support in Safari 4 and Chrome 5\r
+ // (Tested on OSX)\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2010 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.ui.impl;\r
+\r
+import com.google.gwt.dom.client.Element;\r
+\r
+import com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport;\r
+\r
+/**\r
+ * {@link com.allen_sauer.gwt.voices.client.NativeSound} implementation for standard browsers.\r
+ */\r
+public abstract class NativeSoundImplStandard extends NativeSoundImpl {\r
+ // CHECKSTYLE_JAVADOC_OFF\r
+\r
+ @Override\r
+ public native Element createElement(String url)\r
+ /*-{\r
+ var elem = $doc.createElement("object");\r
+ elem.setAttribute("data", url);\r
+ elem.setAttribute("autostart", "true");\r
+ // setting hidden property prevents MIDI playback\r
+ // elem.setAttribute("hidden", "true");\r
+ return elem;\r
+ }-*/;\r
+\r
+ @Override\r
+ public native MimeTypeSupport getMimeTypeSupport(String mimeType)\r
+ /*-{\r
+ var m = navigator.mimeTypes[mimeType];\r
+ // Note, m != null occurs in many browsers for well known MIME types\r
+ // even though the MIME type is not supported without a plug-in\r
+ return (m != null && m.enabledPlugin != null)\r
+ ? @com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport::MIME_TYPE_SUPPORT_READY\r
+ : @com.allen_sauer.gwt.voices.client.SoundController.MimeTypeSupport::MIME_TYPE_NOT_SUPPORTED;\r
+ }-*/;\r
+\r
+ @Override\r
+ public native void setBalance(Element elem, int balance)\r
+ /*-{\r
+ // did not find any browsers actually supporting this\r
+ elem.setAttribute("balance", "" + balance);\r
+ }-*/;\r
+\r
+ @Override\r
+ public native void setVolume(Element elem, int volume)\r
+ /*-{\r
+ elem.setAttribute("volume", "" + volume);\r
+ }-*/;\r
+}\r
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\r
+</head>\r
+<body>\r
+This package contains deferred binding implementations.\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\r
+</head>\r
+<body>\r
+This package contains widgets.\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.util;\r
+\r
+import com.google.gwt.user.client.Element;\r
+\r
+// CHECKSTYLE_JAVADOC_OFF\r
+public class DOMUtil {\r
+ private static int uniqueId = 1000;\r
+\r
+ public static native String getNodeName(Element elem)\r
+ /*-{\r
+ return elem.nodeName;\r
+ }-*/;\r
+\r
+ public static String getUniqueId() {\r
+ return "gwtVoices" + uniqueId++;\r
+ }\r
+}\r
--- /dev/null
+/*\r
+ * Copyright 2009 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except\r
+ * in compliance with the License. You may obtain a copy of the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software distributed under the License\r
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r
+ * or implied. See the License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+package com.allen_sauer.gwt.voices.client.util;\r
+\r
+// CHECKSTYLE_JAVADOC_OFF\r
+public class StringUtil {\r
+ public static boolean contains(String[] arr, String text) {\r
+ for (String element : arr) {\r
+ if (element.equals(text)) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ public static String getSimpleName(Class<?> clazz) {\r
+ String name = clazz.getName();\r
+ return name.substring(name.lastIndexOf('.') + 1);\r
+ }\r
+}\r
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">\r
+<html>\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\r
+</head>\r
+<body>\r
+This package contains library utility classes.\r
+</body>\r
+</html>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/distro-source/core/src/gwt-module.dtd">\r
+ <!--\r
+ Copyright 2007 Fred Sauer Licensed under the Apache License, Version 2.0 (the "License"); you\r
+ may not use this file except in compliance with the License. You may obtain a copy of the\r
+ License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or\r
+ agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the\r
+ specific language governing permissions and limitations under the License.\r
+ -->\r
+<module>\r
+\r
+ <!-- Inherit the core Web Toolkit stuff. -->\r
+ <inherits name='com.google.gwt.user.User' />\r
+\r
+ <source path="client" />\r
+\r
+ <public path="public">\r
+ <exclude name="**/*.as" />\r
+ </public>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImplOpera">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImpl" />\r
+ <when-property-is name="user.agent" value="opera" />\r
+ </replace-with>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImplSafari">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImpl" />\r
+ <when-property-is name="user.agent" value="safari" />\r
+ </replace-with>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImplIE6">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImpl" />\r
+ <any>\r
+ <when-property-is name="user.agent" value="ie6" />\r
+ <when-property-is name="user.agent" value="ie8" />\r
+ <when-property-is name="user.agent" value="ie9" />\r
+ </any>\r
+ </replace-with>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImplMozilla">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImpl" />\r
+ <when-property-is name="user.agent" value="gecko1_8" />\r
+ </replace-with>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImplMozillaOld">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.FlashMovieImpl" />\r
+ <when-property-is name="user.agent" value="gecko" />\r
+ </replace-with>\r
+\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImplOpera">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImpl" />\r
+ <when-property-is name="user.agent" value="opera" />\r
+ </replace-with>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImplSafari">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImpl" />\r
+ <when-property-is name="user.agent" value="safari" />\r
+ </replace-with>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImplIE6">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImpl" />\r
+ <any>\r
+ <when-property-is name="user.agent" value="ie6" />\r
+ <when-property-is name="user.agent" value="ie8" />\r
+ <when-property-is name="user.agent" value="ie9" />\r
+ </any>\r
+ </replace-with>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImplMozilla">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImpl" />\r
+ <when-property-is name="user.agent" value="gecko1_8" />\r
+ </replace-with>\r
+\r
+ <replace-with class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImplMozillaOld">\r
+ <when-type-is class="com.allen_sauer.gwt.voices.client.ui.impl.NativeSoundImpl" />\r
+ <when-property-is name="user.agent" value="gecko" />\r
+ </replace-with>\r
+\r
+</module>\r
--- /dev/null
+/*\r
+ * Copyright 2011 Fred Sauer\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not\r
+ * use this file except in compliance with the License. You may obtain a copy of\r
+ * the License at\r
+ * \r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ * \r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+ * License for the specific language governing permissions and limitations under\r
+ * the License.\r
+ */\r
+ \r
+ // See http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/media/Sound.html\r
+\r
+ package {\r
+ import flash.external.ExternalInterface;\r
+ import flash.media.Sound;\r
+ import flash.display.Sprite;\r
+ import flash.events.Event;\r
+ import flash.events.IOErrorEvent;\r
+ import flash.media.SoundChannel;\r
+ import flash.media.SoundLoaderContext;\r
+ import flash.net.URLRequest;\r
+ import flash.media.SoundTransform;\r
+ \r
+ public class Voices extends Sprite\r
+ {\r
+ private var sounds:Array = [];\r
+ private var volume:Array = [];\r
+ private var panning:Array = [];\r
+ private var channel:Array = [];\r
+ private var loop:Array = [];\r
+ private var domId:String;\r
+ \r
+ public function Voices() {\r
+ ExternalInterface.marshallExceptions = true;\r
+ \r
+ domId = this.root.loaderInfo.parameters.id;\r
+ log("***************************************************************************************************");\r
+ log("Creating Voices '" + domId + "' ...");\r
+ \r
+ log("ExternalInterface.available = " + ExternalInterface.available);\r
+ log("ExternalInterface.objectID = " + ExternalInterface.objectID);\r
+ \r
+ var result:Object;\r
+ \r
+ ExternalInterface.addCallback("createSound", createSound);\r
+ log("addCallback(createSound) -> done");\r
+ \r
+ ExternalInterface.addCallback("playSound", playSound);\r
+ log("addCallback(playSound) -> done");\r
+ \r
+ ExternalInterface.addCallback("stopSound", stopSound);\r
+ log("addCallback(stopSound) -> done");\r
+ \r
+ ExternalInterface.addCallback("setLooping", setLooping);\r
+ log("addCallback(setLooping) -> done");\r
+ \r
+ ExternalInterface.addCallback("setVolume", setVolume);\r
+ log("addCallback(setVolume) -> done");\r
+ \r
+ ExternalInterface.addCallback("setPanning", setPanning);\r
+ log("addCallback(setPanning) -> done");\r
+ \r
+ log("Voices created.");\r
+ \r
+ // notify JavaScript that we are ready\r
+ result = ExternalInterface.call("document.VoicesMovie['" + domId + "'].ready");\r
+ log("document.VoicesMovie['" + domId + "'].ready() -> " + result);\r
+ }\r
+\r
+ private function createSound(id:Number, url:String, checkPolicyFile:Boolean):void {\r
+ var func:String = "createSound(id=" + id + ", url='" + url + "')";\r
+ logStart(func);\r
+\r
+ // default to false\r
+ loop[id] = 0;\r
+\r
+ var bufferTime:Number = 1000; // default\r
+ var context:SoundLoaderContext = new SoundLoaderContext(bufferTime, checkPolicyFile);\r
+\r
+ sounds[id] = new Sound();\r
+ volume[id] = 1;\r
+ panning[id] = 0;\r
+ sounds[id].addEventListener(Event.COMPLETE, function(event:Event):void {\r
+ var func:String = "Event.COMPLETE (=Sound Loaded) id=" + id;\r
+ logStart(func);\r
+ try {\r
+ var result:Object = ExternalInterface.call("document.VoicesMovie['" + domId + "'].soundLoaded", id);\r
+ } catch(e:Error) {\r
+ logError(func, e);\r
+ }\r
+ log("document.VoicesMovie['" + domId + "'].soundLoaded(" + id + ") -> " + result + " // " + typeof result);\r
+ logEnd(func);\r
+ });\r
+ // TODO handle IO_ERROR\r
+// sounds[id].addEventListener(IOErrorEvent.IO_ERROR, function(event:Event):void {\r
+// log("IOErrorEvent.IO_ERROR id=" + id);\r
+// var result:Object = ExternalInterface.call("document.VoicesMovie['" + domId + "'].soundLoaded", id);\r
+// log("document.VoicesMovie['" + domId + "'].soundLoaded(" + id + ") -> " + result);\r
+// });\r
+ sounds[id].load(new URLRequest(url), context);\r
+ logEnd(func);\r
+ }\r
+ \r
+ private function playSound(id:Number):Boolean {\r
+ var func:String = "playSound(id=" + id + ")";\r
+ logStart(func);\r
+ var success:Boolean;\r
+ \r
+ if (channel[id] != null) {\r
+ channel[id].stop();\r
+ }\r
+ var transform:SoundTransform = new SoundTransform(volume[id], panning[id]);\r
+ /*\r
+ * Sound#play() returns null if there is no sound card, or if the maximum number of sound channels (32) has been reached.\r
+ * See http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html#play()\r
+ */\r
+ channel[id] = sounds[id].play(0, loop[id], transform);\r
+ if (channel[id] == null) {\r
+ log("No (more) channel(s) available to play sound id " + id);\r
+ success = false;\r
+ } else {\r
+ channel[id].addEventListener(Event.SOUND_COMPLETE, function(event:Event):void {\r
+ var func:String = "Event.SOUND_COMPLETE (=Playback Completed) id=" + id;\r
+ logStart(func);\r
+ var method:String = "document.VoicesMovie['" + domId + "'].playbackCompleted";\r
+ try {\r
+ var result:Object = ExternalInterface.call(method, id);\r
+ logResult(method, result);\r
+ } catch(e:Error) {\r
+ logError(method, e)\r
+ }\r
+ });\r
+ success = true;\r
+ }\r
+ logEnd(func);\r
+ return success;\r
+ }\r
+ \r
+ private function stopSound(id:Number):void {\r
+ var func:String = "stopSound(id=" + id + ")";\r
+ logStart(func);\r
+ channel[id].stop();\r
+ logEnd(func);\r
+ }\r
+\r
+ private function setLooping(id:Number, loopCount:Number):void {\r
+ var func:String = "setLooping(id=" + id + ", loopCount=" + loopCount + ")";\r
+ logStart(func);\r
+ loop[id] = loopCount;\r
+ logEnd(func);\r
+ }\r
+ \r
+ private function setVolume(id:Number, volume:Number):void {\r
+ var func:String = "setVolume(id=" + id + ", volume=" + volume + "%)";\r
+ logStart(func);\r
+ this.volume[id] = volume;\r
+ logEnd(func);\r
+ }\r
+ \r
+ private function setPanning(id:Number, panning:Number):void {\r
+ var func:String = "setPanning(id=" + id + ", panning=" + panning + ")";\r
+ logStart(func);\r
+ this.panning[id] = panning;\r
+ logEnd(func);\r
+ }\r
+\r
+ private function logStart(func:String):void {\r
+ log(">> " + func);\r
+ }\r
+\r
+ private function logEnd(func:String):void {\r
+ log("<< " + func);\r
+ }\r
+\r
+ private function logResult(method:String, result:Object):void {\r
+ log(method + " -> " + (typeof result) + ": " + result);\r
+ }\r
+ \r
+ private function logError(func:String, e:Error):void {\r
+ log(func + " -> ERROR(" + e + "): " + e.getStackTrace());\r
+ }\r
+ \r
+ private function log(text:String):void {\r
+ //ExternalInterface.call("document.VoicesMovie['" + this.root.loaderInfo.parameters.id + "'].log", text);\r
+ }\r
+ }\r
+}
\ No newline at end of file
<module rename-to='com.badlogic.gdx.backends.gwt'>\r
<inherits name='com.google.gwt.user.User' />\r
<inherits name="com.badlogic.gdx" />\r
- <inherits name="com.google.gwt.typedarrays.TypedArrays"/>\r
- <inherits name="com.google.gwt.webgl.WebGL"/>\r
+ <inherits name='com.allen_sauer.gwt.voices.gwt-voices' />\r
+ <inherits name="com.google.gwt.typedarrays.TypedArrays" />\r
+ <inherits name="com.google.gwt.webgl.WebGL" />\r
\r
<super-source path="gwt/emu" />\r
<source path="gwt">\r
Gdx.audio = new GwtAudio();\r
\r
// tell listener about app creation\r
- listener.create();\r
- listener.resize(graphics.getWidth(), graphics.getHeight());\r
+ try {\r
+ listener.create();\r
+ listener.resize(graphics.getWidth(), graphics.getHeight());\r
+ } catch(Throwable t) {\r
+ error("GwtApplication", "exception: " + t.getMessage(), t);\r
+ t.printStackTrace();\r
+ throw new RuntimeException(t);\r
+ }\r
\r
// add resize handler to canvas\r
// FIXME\r
((GwtInput)Gdx.input).justTouched = false;\r
} catch (Throwable t) {\r
error("GwtApplication", "exception: " + t.getMessage(), t);\r
- t.printStackTrace();\r
+ throw new RuntimeException(t);\r
}\r
}\r
}.scheduleRepeating((int)((1f / config.fps) * 1000));\r
protected AgentInfo () {\r
}\r
}\r
+\r
+ public String getBaseUrl () {\r
+ return preloader.baseUrl;\r
+ }\r
}\r
package com.badlogic.gdx.backends.gwt;\r
\r
+import com.allen_sauer.gwt.voices.client.SoundController;\r
import com.badlogic.gdx.Audio;\r
+import com.badlogic.gdx.Gdx;\r
import com.badlogic.gdx.audio.AudioDevice;\r
import com.badlogic.gdx.audio.AudioRecorder;\r
import com.badlogic.gdx.audio.Music;\r
import com.badlogic.gdx.utils.GdxRuntimeException;\r
\r
public class GwtAudio implements Audio {\r
+ SoundController soundController;\r
+ \r
+ public GwtAudio() {\r
+ soundController = new SoundController();\r
+ soundController.setGwtVoicesSwfLocation(((GwtApplication)Gdx.app).getBaseUrl());\r
+ }\r
+ \r
@Override\r
public AudioDevice newAudioDevice (int samplingRate, boolean isMono) {\r
throw new GdxRuntimeException("AudioDevice not supported by GWT backend");\r
\r
@Override\r
public Sound newSound (FileHandle fileHandle) {\r
- return new GwtSound();\r
+ return new GwtSound(soundController, fileHandle);\r
}\r
\r
@Override\r
public Music newMusic (FileHandle file) {\r
- return new GwtMusic();\r
+ return new GwtMusic(soundController, file);\r
}\r
}\r
package com.badlogic.gdx.backends.gwt;\r
\r
+import com.allen_sauer.gwt.voices.client.Sound;\r
+import com.allen_sauer.gwt.voices.client.SoundController;\r
+import com.allen_sauer.gwt.voices.client.handler.PlaybackCompleteEvent;\r
+import com.allen_sauer.gwt.voices.client.handler.SoundHandler;\r
+import com.allen_sauer.gwt.voices.client.handler.SoundLoadStateChangeEvent;\r
+import com.badlogic.gdx.Gdx;\r
import com.badlogic.gdx.audio.Music;\r
+import com.badlogic.gdx.files.FileHandle;\r
\r
public class GwtMusic implements Music {\r
+ Sound sound;\r
+ boolean isPlaying = false;\r
+ boolean isLooping = false;\r
+ \r
+ public GwtMusic (SoundController soundController, FileHandle file) {\r
+ String url = ((GwtApplication)Gdx.app).getBaseUrl() + file.path();\r
+ String mimeType = getMimeType(file.extension());\r
+ sound = soundController.createSound(mimeType, url, true, false);\r
+ sound.addEventHandler(new SoundHandler() {\r
+ @Override\r
+ public void onSoundLoadStateChange (SoundLoadStateChangeEvent event) {\r
+ if(isPlaying) sound.play();\r
+ }\r
+ \r
+ @Override\r
+ public void onPlaybackComplete (PlaybackCompleteEvent event) {\r
+ isPlaying = false;\r
+ }\r
+ });\r
+ }\r
+ \r
+ private String getMimeType(String extension) {\r
+ if(extension.toLowerCase().equals("wav")) return Sound.MIME_TYPE_AUDIO_WAV_PCM;\r
+ if(extension.toLowerCase().equals("ogg")) return Sound.MIME_TYPE_AUDIO_OGG_VORBIS;\r
+ if(extension.toLowerCase().equals("mp3")) return Sound.MIME_TYPE_AUDIO_MPEG_MP3;\r
+ return Sound.MIME_TYPE_AUDIO_WAV_UNKNOWN; // FIXME not really possible...\r
+ }\r
+\r
@Override\r
public void play () {\r
- // TODO Auto-generated method stub\r
-\r
+ if(isPlaying) return;\r
+ sound.play();\r
+ isPlaying = true;\r
}\r
\r
@Override\r
public void pause () {\r
- // TODO Auto-generated method stub\r
-\r
+ sound.stop();\r
+ isPlaying = false;\r
}\r
\r
@Override\r
public void stop () {\r
- // TODO Auto-generated method stub\r
-\r
+ sound.stop();\r
+ isPlaying = false;\r
}\r
\r
@Override\r
public boolean isPlaying () {\r
- // TODO Auto-generated method stub\r
- return false;\r
+ return isPlaying;\r
}\r
\r
@Override\r
public void setLooping (boolean isLooping) {\r
- // TODO Auto-generated method stub\r
-\r
+ sound.setLooping(isLooping);\r
+ this.isLooping = isLooping;\r
}\r
\r
@Override\r
public boolean isLooping () {\r
- // TODO Auto-generated method stub\r
- return false;\r
+ return isLooping;\r
}\r
\r
@Override\r
public void setVolume (float volume) {\r
- // TODO Auto-generated method stub\r
-\r
+ sound.setVolume((int)(100 * volume));\r
}\r
\r
@Override\r
public float getPosition () {\r
- // TODO Auto-generated method stub\r
return 0;\r
}\r
\r
@Override\r
public void dispose () {\r
- // TODO Auto-generated method stub\r
-\r
}\r
}\r
package com.badlogic.gdx.backends.gwt;\r
\r
+import com.allen_sauer.gwt.voices.client.SoundController;\r
+import com.badlogic.gdx.Gdx;\r
import com.badlogic.gdx.audio.Sound;\r
+import com.badlogic.gdx.files.FileHandle;\r
\r
public class GwtSound implements Sound {\r
+ com.allen_sauer.gwt.voices.client.Sound sound;\r
+ \r
+ public GwtSound (SoundController soundController, FileHandle file) {\r
+ String url = ((GwtApplication)Gdx.app).getBaseUrl() + file.path();\r
+ String mimeType = getMimeType(file.extension());\r
+ sound = soundController.createSound(mimeType, url, false, false);\r
+ }\r
+ \r
+ private String getMimeType(String extension) {\r
+ if(extension.toLowerCase().equals("wav")) return com.allen_sauer.gwt.voices.client.Sound.MIME_TYPE_AUDIO_WAV_PCM;\r
+ if(extension.toLowerCase().equals("ogg")) return com.allen_sauer.gwt.voices.client.Sound.MIME_TYPE_AUDIO_OGG_VORBIS;\r
+ if(extension.toLowerCase().equals("mp3")) return com.allen_sauer.gwt.voices.client.Sound.MIME_TYPE_AUDIO_MPEG_MP3;\r
+ return com.allen_sauer.gwt.voices.client.Sound.MIME_TYPE_AUDIO_WAV_UNKNOWN; // FIXME not really possible...\r
+ }\r
+\r
@Override\r
public long play () {\r
- // TODO Auto-generated method stub\r
+ sound.play();\r
return 0;\r
}\r
\r
@Override\r
public long play (float volume) {\r
- // TODO Auto-generated method stub\r
+ sound.play();\r
+ sound.setVolume(((int)volume * 100));\r
return 0;\r
}\r
\r
\r
@Override\r
public void dispose () {\r
- // TODO Auto-generated method stub\r
-\r
}\r
\r
@Override\r
@Override\r
public void setLooping (long soundId, boolean looping) {\r
// TODO Auto-generated method stub\r
-\r
}\r
\r
@Override\r
@Override\r
public void setPan (long soundId, float pan, float volume) {\r
// TODO Auto-generated method stub\r
-\r
}\r
-\r
}\r
}\r
}\r
\r
- final String baseUrl;\r
+ public final String baseUrl;\r
\r
public Preloader() {\r
baseUrl = GWT.getModuleBaseURL().replace(GWT.getModuleName() + "/", "");\r