Skip to content
This repository has been archived by the owner on Dec 11, 2017. It is now read-only.

GettingStarted

Fred Sauer edited this page Dec 10, 2017 · 4 revisions

Table of Contents

GWT Compatibility

Please make sure your you GWT and gwt-voices versions are compatible.

If you are using GWT version... Then use gwt-voices version...
GWT 2.6.1 or newer gwt-voices-3.3.2.jar
GWT 2.6.0 or newer gwt-voices-3.3.0.jar
GWT 2.5.0 or newer gwt-voices-3.2.0.jar
GWT 2.4.0 or newer gwt-voices-2.1.8.jar
GWT 2.3.0 gwt-voices-2.1.2.jar
GWT 2.2.0 gwt-voices-2.0.0.jar
GWT 2.1.1 gwt-voices-2.0.0.jar
GWT 2.1.0 gwt-voices-2.0.0.jar
GWT 2.0 gwt-voices-1.7.0.jar
GWT 1.7.0 gwt-voices-1.6.0.jar
GWT 1.6.4 gwt-voices-1.6.0.jar
GWT 1.5.3 gwt-voices-1.5.5.jar
GWT 1.5.2 gwt-voices-1.5.5.jar
GWT 1.5.1 (1.5 RC2) gwt-voices-1.5.5.jar
GWT 1.5.0 (1.5 RC1) gwt-voices-1.5.5.jar
GWT 1.4.62 (1.4 update 2) gwt-voices-1.0.0.jar
GWT 1.4.61 (1.4 update) gwt-voices-1.0.0.jar
GWT 1.4.60 (1.4) gwt-voices-1.0.0.jar

Examples & Demos

Working examples

Adding the gwt-voices Module to your Eclipse Project

  1. Download the latest gwt-voices-<version>.jar. * Note for Maven users: Releases are also available in Maven Central with groupId com.allen-sauer.gwt.voices and artifact gwt-voices.
  2. Create a GWT Eclipse project as instructed here: http://www.gwtproject.org/usingeclipse.html#creating
  3. Add the gwt-voices jar to your project via one of these two methods: * Right-click on the project node in the Package Explorer and select 'Build Path > Add External Archives...'. Then, specify the location of the gwt-voices-<version>.jar file. * Copy the gwt-voices-<version>.jar file into your project's war/WEB-INF/lib directory. Then, in the Project Explorer view, right click the jar file and select 'Build Path > Add to Build Path'
  4. Make sure the GWT compiler can find the gwt-voices source code. Modify your *.gwt.xml module to inherit gwt-dnd support:
  <!-- Inherit sound support -->
  <inherits name='com.allen_sauer.gwt.voices.gwt-voices'/>
  1. (Optional) if you want to play with the demo (examples), you'll need to grab those from the demo directory as there is no jar file for the demos (i.e. the com.allen_sauer.gwt.voice.demo package). See the Using Source with Eclipse wiki for more details.

A Quick Example

package com.mycompany.client;

import com.google.gwt.core.client.EntryPoint;

import com.allen_sauer.gwt.voices.client.Sound;
import com.allen_sauer.gwt.voices.client.SoundController;

public class SimpleApplication implements EntryPoint {
  public void onModuleLoad() {
    SoundController soundController = new SoundController();
    Sound sound = soundController.createSound(Sound.MIME_TYPE_AUDIO_MPEG,
        "http(s)/url/to/your/sound/file.mp3");

    sound.play();
  }
}

A More Elaborate Example

package com.mycompany.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

import com.allen_sauer.gwt.voices.client.Sound;
import com.allen_sauer.gwt.voices.client.SoundController;
import com.allen_sauer.gwt.voices.client.handler.PlaybackCompleteEvent;
import com.allen_sauer.gwt.voices.client.handler.SoundHandler;
import com.allen_sauer.gwt.voices.client.handler.SoundLoadStateChangeEvent;

public class MyApplication implements EntryPoint {
  public void onModuleLoad() {
    // set uncaught exception handler
    GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
      public void onUncaughtException(Throwable throwable) {
        String text = "Uncaught exception: ";
        while (throwable != null) {
          StackTraceElement[] stackTraceElements = throwable.getStackTrace();
          text += throwable.toString() + "\n";
          for (int i = 0; i < stackTraceElements.length; i++) {
            text += "    at " + stackTraceElements[i] + "\n";
          }
          throwable = throwable.getCause();
          if (throwable != null) {
            text += "Caused by: ";
          }
        }
        DialogBox dialogBox = new DialogBox(true, false);
        DOM.setStyleAttribute(dialogBox.getElement(), "backgroundColor", "#ABCDEF");
        System.err.print(text);
        text = text.replaceAll(" ", "&nbsp;");
        dialogBox.setHTML("<pre>" + text + "</pre>");
        dialogBox.center();
      }
    });

    // use a deferred command so that the handler catches onModuleLoad2() exceptions
    DeferredCommand.addCommand(new Command() {
      public void execute() {
        onModuleLoad2();
      }
    });
  }

  private void onModuleLoad2() {
    // create a (disabled) play button
    final Button playButton = new Button("click to play");

    // create a place holder for the load state
    final HTML loadStateHTML = new HTML();

    // create sound controller
    SoundController soundController = new SoundController();

    // create a sound
    final Sound sound = soundController.createSound(Sound.MIME_TYPE_AUDIO_MPEG,
        "url/to/your/sound/file.mp3");

    // add a sound handler so we know when the sound has loaded
    sound.addEventHandler(new SoundHandler() {

      public void onPlaybackComplete(PlaybackCompleteEvent event) {
        // WARNING: this method may in fact never be called; see Sound.LoadState
      }

      public void onSoundLoadStateChange(SoundLoadStateChangeEvent event) {
      // See detailed documentation in Sound.LoadState
      // in order to understand these possible values:
      // LOAD_STATE_SUPPORTED_AND_READY
      // LOAD_STATE_SUPPORTED_NOT_READY
      // LOAD_STATE_SUPPORTED_MAYBE_READY
      // LOAD_STATE_NOT_SUPPORTED
      // LOAD_STATE_SUPPORT_NOT_KNOWN
      // LOAD_STATE_UNINITIALIZED

        loadStateHTML.setHTML("Load state: " + event.getLoadStateAsString());
      }
    });

    // when we click, play the sound
    playButton.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        sound.play();
      }
    });

    // add the button
    RootPanel.get().add(playButton);

    // add the load state status
    RootPanel.get().add(loadStateHTML);
  }
}

Load State Support

Due to browser limitations it is sometimes not possible to know if a certain sound will in fact play or has played. However, gwt-voices does provide you information on what level of detail is available for each sound file. This will vary by browser, by platform, by MIME type and by available plugins supporting audio. Here's the relevant documentation from Sound.java.

  enum LoadState {
    /**
     * Play back of this sound's MIME type is known NOT to be supported.
     * Calling {@link Sound#play()} may in rare occasions still work.
     */
    LOAD_STATE_NOT_SUPPORTED,

    /**
     * The sound load state is unknown, and cannot be determined. Hope for the best.
     * Calling {@link Sound#play()} may or may not work.
     */
    LOAD_STATE_SUPPORT_NOT_KNOWN,

    /**
     * Play back of this sound's MIME type is supported and this sound object
     * is ready for immediate play back. This means that the sound file has either
     * been downloaded by the client (when not streaming), or is ready for streaming.
     */
    LOAD_STATE_SUPPORTED_AND_READY,

    /**
     * Play back of this sound's MIME type is known to be supported, however
     * the client browser is unable to provide load notification events. It
     * cannot be programmatically determined when the client has downloaded
     * the sound. When possible, an attempt will be made to begin downloading
     * the sound file in the background. The load state will NOT change to
     * {@link LoadState#LOAD_STATE_SUPPORTED_AND_READY}, or indeed to any other state.
     */
    LOAD_STATE_SUPPORTED_MAYBE_READY,

    /**
     * Play back of this sound's MIME type is known to be supported,
     * however the sound file has not yet been loaded. The load state is at some
     * point expected to change to {@link LoadState#LOAD_STATE_SUPPORTED_AND_READY}.
     */
    LOAD_STATE_SUPPORTED_NOT_READY,

    /**
     * All new sounds start in this load state, after which they will transition
     * at least once to a new load state.
     */
    LOAD_STATE_UNINITIALIZED,
  };

Debugging

gwt-voices uses one of several methods to playback audio, depending on your browser, your operating system, which extensions and plugins are installed and what the MIME Type is of the file your're trying to play. In order in which playback techniques are attempted can be influenced via code:

// Try HTML5 first
soundController.setPreferredSoundTypes(SoundType.HTML5);

// Prefer Web Audio, then try Flash, and then HTML5
soundController.setPreferredSoundTypes(SoundType.WEB_AUDIO, SoundType.FLASH, SoundType.HTML5);

or via the gwt-voices URL query parameter, which you can appended to the end of your URLs:

You can see all these implementations at work here:

http://allen-sauer.com/com.allen_sauer.gwt.voices.demo.VoicesDemo/VoicesDemo.html

Out of the box, Web Audio is tried first on supported browsers (currently Chrome and Webkit nightlies). Flash is tried next, because it's the next most reliable choice for audio playback. When Flash is not available, HTML5 Audio (via the AUDIO element) is used as a fallback, or native (think BGSOUND) browser support.

You can confirm which Audio implementation is being used by expecting the DOM after you begun using gwt-voices. You can use the Chrome Developer tools or Firebug and friends to look at the DOM.

  • In the Web Audio case you'll see an empty DIV like this at end of the document BODY:
<div style="position: absolute; overflow: hidden; left: -500px; top: -500px; width: 0px; height: 0px; "></div>
  • In the HTML5 Audio case you'll see the same empty DIV, followed by an AUDIO tag for each individual sound:
<div style="position: absolute; overflow: hidden; left: -500px; top: -500px; width: 0px; height: 0px; "></div>
<audio tabindex="0" src="path/to/your/foo.mp3"></audio>
<audio tabindex="0" src="path/to/your/bar.mp3"></audio>
<audio tabindex="0" src="path/to/your/funny.mp3"></audio>
<audio tabindex="0" src="path/to/your/laugh.mp3"></audio>
<audio tabindex="0" src="path/to/your/chirp.mp3"></audio>
  • In the Flash case you'll see that the DIV contains an OBJECT tag with id gwtVoices1000, like this:
<div style="position: absolute; overflow: hidden; left: -500px; top: -500px; width: 0px; height: 0px; ">
  <object id="gwtVoices1000" type="application/x-shockwave-flash"
          data="http://chrome.angrybirds.com/fowl/gwt-voices.swf">
     <param name="FlashVars" value="id=gwtVoices1000">
  </object>
</div>
  • In the native case you'll see that the DIV contains OBJECT or BGSOUND elements for each audio file, like this:
<div style="position: absolute; overflow: hidden; left: -500px; top: -500px; width: 0px; height: 0px; ">
  <object data="freesoundproject/9874__vixuxx__crow.au" autostart="true" volume="0"></object>
</div>

Restrictions

iOS Restrictions

See http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW1 for details.

Steaming MP3 Restrictions

The advantage of using streaming to play MP3 files is that playback can begin before the entire file is downloaded by the client. There are a couple of disadvantages to streaming audio:

  1. The download (or audio stream) may be halted or temporarily interrupted, causing playback to be incomplete
  2. It's not possible to detect whether a particular audio stream exists without attempting to retrieve (playback) that stream * Because of this, gwt-voices assumes LOAD_STATE_SUPPORTED_AND_READY for any streaming MP3 file when in fact there may be no such MP3 file available at the designated URL and attempting to playback the file may fail * Currently there's no way for a Sound object to indicate that download has begun. We can only detect when download is complete. As such the only way to detect the existence of an MP3 stream at a given URL is to playback/download the entire audio file, which defeats the purpose of streaming, and may in fact not be possible for 'infinite' streams.

Tips

  • Make sure you use an UncaughtExceptionHandler so that unexpected exceptions are not lost in Production Mode