001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.markerlayer;
003
004import java.awt.event.ActionEvent;
005import java.io.IOException;
006import java.net.URL;
007import java.util.Collections;
008
009import org.openstreetmap.josm.data.coor.LatLon;
010import org.openstreetmap.josm.data.gpx.GpxConstants;
011import org.openstreetmap.josm.data.gpx.GpxLink;
012import org.openstreetmap.josm.data.gpx.WayPoint;
013import org.openstreetmap.josm.gui.MainApplication;
014import org.openstreetmap.josm.io.audio.AudioPlayer;
015import org.openstreetmap.josm.io.audio.AudioUtil;
016import org.openstreetmap.josm.tools.template_engine.TemplateEngineDataProvider;
017
018/**
019 * Marker class with audio playback capability.
020 *
021 * @author Frederik Ramm
022 *
023 */
024public class AudioMarker extends ButtonMarker {
025
026    private final URL audioUrl;
027    private static volatile AudioMarker recentlyPlayedMarker;
028    public double syncOffset;
029    public boolean timeFromAudio; // as opposed to from the GPX track
030
031    public AudioMarker(LatLon ll, TemplateEngineDataProvider dataProvider, URL audioUrl, MarkerLayer parentLayer, double time, double offset) {
032        super(ll, dataProvider, /* ICON(markers/) */ "speech", parentLayer, time, offset);
033        this.audioUrl = audioUrl;
034        this.syncOffset = 0.0;
035        this.timeFromAudio = false;
036    }
037
038    @Override
039    public void actionPerformed(ActionEvent ev) {
040        play();
041    }
042
043    /**
044     * Returns the marker played the most recently, if any.
045     * @return the marker played the most recently, or {@code null}
046     */
047    public static AudioMarker recentlyPlayedMarker() {
048        return recentlyPlayedMarker;
049    }
050
051    /**
052     * Forgets the marker played the most recently, if any.
053     */
054    static void resetRecentlyPlayedMarker() {
055        recentlyPlayedMarker = null;
056    }
057
058    public URL url() {
059        return audioUrl;
060    }
061
062    /**
063     * Starts playing the audio associated with the marker offset by the given amount
064     * @param after : seconds after marker where playing should start
065     */
066    public void play(double after) {
067        try {
068            // first enable tracing the audio along the track
069            MainApplication.getMap().mapView.playHeadMarker.animate();
070
071            AudioPlayer.play(audioUrl, offset + syncOffset + after);
072            recentlyPlayedMarker = this;
073        } catch (IOException | InterruptedException e) {
074            AudioUtil.audioMalfunction(e);
075        }
076    }
077
078    /**
079     * Starts playing the audio associated with the marker: used in response to pressing
080     * the marker as well as indirectly
081     *
082     */
083    public void play() {
084        play(0.0);
085    }
086
087    public void adjustOffset(double adjustment) {
088        syncOffset = adjustment; // added to offset may turn out negative, but that's ok
089    }
090
091    public double syncOffset() {
092        return syncOffset;
093    }
094
095    @Override
096    protected String getTextTemplateKey() {
097        return "markers.audio.pattern";
098    }
099
100    @Override
101    public WayPoint convertToWayPoint() {
102        WayPoint wpt = super.convertToWayPoint();
103        GpxLink link = new GpxLink(audioUrl.toString());
104        link.type = "audio";
105        wpt.put(GpxConstants.META_LINKS, Collections.singleton(link));
106        wpt.getExtensions().add("josm", "offset", Double.toString(offset));
107        wpt.getExtensions().add("josm", "sync-offset", Double.toString(syncOffset));
108        return wpt;
109    }
110}