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}