package de.tud.kom.socom.web.client.htmlelements;
import java.util.Date;
import com.chj.gwt.client.soundmanager2.Callback;
import com.chj.gwt.client.soundmanager2.Option;
import com.chj.gwt.client.soundmanager2.SMSound;
import com.chj.gwt.client.soundmanager2.SoundManager;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.uibinder.client.UiTemplate;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import de.tud.kom.socom.web.client.util.FileUtils;
import de.tud.kom.socom.web.client.util.DateTimeUtils.Units;
import de.tud.kom.socom.web.client.util.FileUtils.FileState;
/** Provides the GWT implementation of an AudioControl with several DIVs for Play, Stop, Progress and Time
*
* @author jkonert
*
*/
public class AudioControl extends Composite {
@UiTemplate("AudioControl.ui.xml")
interface AudioControlUiBinder extends UiBinder<Widget, AudioControl> { }
private static AudioControlUiBinder uiBinder = GWT.create(AudioControlUiBinder.class);
@UiField protected HTMLPanel audioControl; // the whole control field
@UiField protected Label audioPlay;
@UiField protected Label audioStop;
@UiField protected DivElement audioProgressCurrent; // basically only set width from 0 to 100
@UiField protected DivElement audioTime; // may be filled with 00:00:00 / 00:00:00 information
@UiField protected DivElement audioLoading; // may be filled with "Loading..." information
@UiField AudioStyle style; // mapping the styles from xml file (only the onces defined in interface)
interface AudioStyle extends CssResource
{
String audioStopDisabled();
String audioPlayDisabled();
}
private String filePath;
private SoundManager soundManager;
private String soundID;
private SMSound sound;
private NumberFormat digits = NumberFormat.getFormat("00");
private boolean playEnabled = true;
private boolean stopEnabled = false;
private boolean playing = false;
private boolean paused = false;
private long totalMillis = -1;
private long currentMillis = -1;
private boolean isBlinkLoading = false;
public AudioControl()
{
initWidget(uiBinder.createAndBindUi(this));
}
public AudioControl(String filePath)
{
this();
this.setFilePath(filePath);
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
@UiHandler("audioPlay")
protected void onPlayButtonClicked(ClickEvent event)
{
if (!playEnabled) return;
if (isPlaying()) pause();
else if (paused) resume();
else play();
}
@UiHandler("audioStop")
protected void onStopButtonClicked(ClickEvent event)
{
if (!stopEnabled) return;
if (playing || paused) stop();
}
public void play()
{
startLoading();
soundManager.onReady(new Callback(){
public void execute(){
soundManager.play(soundID);
playing = true;
paused = false;
setEnableStopButton(true);
}
});
}
public void stop()
{
//startLoading();
if (soundManager == null) return;
soundManager.onReady(new Callback(){
public void execute(){
soundManager.stop(soundID);
playing = false;
paused = false;
setEnableStopButton(false);
audioTime.setInnerText("");
audioProgressCurrent.getStyle().setWidth(0, Unit.PX);
stopBlinkLoading();
}
});
}
public void pause()
{
if (!playing) return;
startLoading();
soundManager.onReady(new Callback(){
public void execute(){
soundManager.pause(soundID);
playing = false;
paused = true;
}
});
}
public void resume()
{
if (playing || !paused) return;
startLoading();
soundManager.onReady(new Callback(){
public void execute(){
soundManager.resume(soundID);
playing = true;
paused = false;
}
});
}
public void startLoading()
{
if (getReadyState().equals(FileState.error))
{ // on error clear everything and restart
this.soundManager.destroySound(this.soundID);
this.sound = null;
this.soundManager = null;
}
if (soundManager == null)
{
soundManager = SoundManager.quickStart();
soundID = HTMLPanel.createUniqueId();
soundManager.onReady(new Callback(){
public void execute() {
// see Documentation at http://www.schillmania.com/projects/soundmanager2/doc/#sound-object-properties
// or https://github.com/rcaloras/gwt-soundmanager2
soundManager.createSound(new Option[] {
new Option("id",soundID),
new Option("url", filePath),
new Option("stream", true),
new Option("whileloading", new Callback() {
public void execute() {AudioControl.this.blinkLoading();}
}),
new Option("whileplaying",new Callback() {
public void execute() {AudioControl.this.updateTimes();}
}),
new Option("onfinish",new Callback() {
public void execute() {AudioControl.this.stop();}
})
// new Option("onstop", new Callback(){
// public void execute() {AudioControl.this.stop();}
// })
});
AudioControl.this.sound = soundManager.getSoundById(soundID);
};
});
}
}
public boolean isPlaying()
{
if (getReadyState().equals(FileUtils.FileState.error))
{
stop();
}
return playing;
}
/**
*
* @return returns the current state of a file to load/show. If not yet tried to play, the state is always uninitialized
*/
public FileState getReadyState() {
if (soundManager != null && this.sound != null)
{
switch (this.sound.getReadyState())
{// (0=uninitialized, 1=loading, 2=error, 3=success/loaded)
case 1: return FileState.loading;
case 2: return FileState.error;
case 3: return FileState.success;
case 0: // fall-through
default:
// fall-through
}
}
return FileState.uninitialized;
}
/**
* @return -1 if no sound is loaded yet, total time in millis otherwiese
*/
public long getMillisTotalTime()
{
return totalMillis;
}
/**
* @return -1 if no sound is loaded yet, current time index of song otherwise (updated only each second)
*/
public long getMillisCurrentTime()
{
return currentMillis;
}
private void setEnableStopButton(boolean enable)
{
if (enable)
{
audioStop.removeStyleName(style.audioStopDisabled());
stopEnabled = true;
}
else
{
audioStop.addStyleName(style.audioStopDisabled());
stopEnabled = false;
}
}
// called by soundfile loop itself
private void updateTimes()
{
if (sound == null) return;
double width = 0;
long dur = sound.getDuration();
if (dur <= 0 ) dur = sound.getDurationEstimate();
if (dur > 0)
{
long position = sound.getPosition();
width = (position/((double)dur))*100;
audioProgressCurrent.getStyle().setWidth(width, Unit.PCT);
long s = Units.SECONDS.toNextUnitRest(position);
long m = Units.SECONDS.toNextUnit(position);
long h = Units.MINUTES.toNextUnit(m);
m = Units.MINUTES.toNextUnitRest(m);
long S = Units.SECONDS.toNextUnitRest(dur);
long M = Units.SECONDS.toNextUnit(dur);
long H = Units.MINUTES.toNextUnit(M);
M = Units.MINUTES.toNextUnitRest(M);
audioTime.setInnerText(digits.format(h)+":"+digits.format(m)+":"+digits.format(s)+" / "+digits.format(H)+":"+digits.format(M)+":"+digits.format(S));
totalMillis = dur * 1000; //commes in seconds, we need millis
currentMillis = position * 1000; //commes in seconds, we need millis
}
if (sound.getLoaded()) stopBlinkLoading();
}
private void blinkLoading()
{
long res = new Date().getTime()%100;
if ( res > 0 && res < 70) audioLoading.addClassName("hidden");
else
{
audioLoading.removeClassName("hidden");
isBlinkLoading = true;
}
}
private void stopBlinkLoading()
{
if (isBlinkLoading )
{
audioLoading.addClassName("hidden");
isBlinkLoading=false;
}
}
}