]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/correlate/AudioCorrelator.java
Version 19, May 2018
[GpsPrune.git] / tim / prune / correlate / AudioCorrelator.java
1 package tim.prune.correlate;
2
3 import java.awt.FlowLayout;
4 import java.awt.GridLayout;
5
6 import javax.swing.JOptionPane;
7 import javax.swing.JPanel;
8 import javax.swing.JTable;
9
10 import tim.prune.App;
11 import tim.prune.DataSubscriber;
12 import tim.prune.I18nManager;
13 import tim.prune.UpdateMessageBroker;
14 import tim.prune.config.TimezoneHelper;
15 import tim.prune.data.AudioClip;
16 import tim.prune.data.AudioList;
17 import tim.prune.data.DataPoint;
18 import tim.prune.data.MediaObject;
19 import tim.prune.data.MediaList;
20 import tim.prune.data.TimeDifference;
21 import tim.prune.data.Timestamp;
22 import tim.prune.data.TimestampUtc;
23 import tim.prune.undo.UndoCorrelateAudios;
24
25 /**
26  * Class to manage the automatic correlation of audio clips to points
27  * which is very similar to the PhotoCorrelator apart from the clip lengths
28  */
29 public class AudioCorrelator extends Correlator
30 {
31         private AudioTimestampSelector _fileTimesSelector = null, _correlTimesSelector = null;
32
33
34         /**
35          * Constructor
36          * @param inApp App object
37          */
38         public AudioCorrelator(App inApp) {
39                 super(inApp);
40         }
41
42         /**
43          * @return name key
44          */
45         public String getNameKey() {
46                 return "function.correlateaudios";
47         }
48
49         /** @return type key */
50         protected String getMediaTypeKey() {
51                 return "audio";
52         }
53
54         /** @return photo list*/
55         protected MediaList getMediaList() {
56                 return _app.getTrackInfo().getAudioList();
57         }
58
59
60         /**
61          * @return first gui panel including timestamp specification (beginning, middle, end)
62          */
63         protected JPanel makeFirstPanel()
64         {
65                 // First panel for timestamp stuff
66                 JPanel card1 = new JPanel();
67                 card1.setLayout(new FlowLayout(FlowLayout.CENTER));
68                 JPanel grid1 = new JPanel();
69                 grid1.setLayout(new GridLayout(0, 1));
70                 _fileTimesSelector = new AudioTimestampSelector("dialog.correlate.filetimes", "dialog.correlate.filetimes2");
71                 grid1.add(_fileTimesSelector);
72                 _correlTimesSelector = new AudioTimestampSelector("dialog.correlate.correltimes", null);
73                 grid1.add(_correlTimesSelector);
74                 card1.add(grid1);
75                 return card1;
76         }
77
78
79         /**
80          * @return array of boolean flags denoting availability of cards
81          */
82         protected boolean[] getCardEnabledFlags()
83         {
84                 boolean[] cards = super.getCardEnabledFlags();
85                 cards[0] = getAudioLengthAvailability(_app.getTrackInfo().getAudioList());
86                 return cards;
87         }
88
89         /**
90          * @param inAudios AudioList object
91          * @return true if there are any audio lengths available
92          */
93         private static boolean getAudioLengthAvailability(AudioList inAudios)
94         {
95                 for (int i=0; i<inAudios.getNumMedia(); i++)
96                 {
97                         AudioClip a = inAudios.getAudio(i);
98                         if (a.getLengthInSeconds() > 0) {return true;}
99                 }
100                 return false;
101         }
102
103         /**
104          * Create a preview of the correlate action using the selected time difference
105          * @param inTimeDiff TimeDifference to use for preview
106          * @param inShowWarning true to show warning if all points out of range
107          */
108         protected void createPreview(TimeDifference inTimeDiff, boolean inShowWarning)
109         {
110                 TimeDifference timeLimit = parseTimeLimit();
111                 double angDistLimit = parseDistanceLimit();
112                 MediaPreviewTableModel model = new MediaPreviewTableModel("dialog.correlate.select.audioname");
113                 AudioList audios = _app.getTrackInfo().getAudioList();
114                 // Loop through audios deciding whether to set correlate flag or not
115                 int numAudios = audios.getNumAudios();
116                 for (int i=0; i<numAudios; i++)
117                 {
118                         AudioClip audio = audios.getAudio(i);
119                         PointMediaPair pair = getPointPairForMedia(_app.getTrackInfo().getTrack(), audio, inTimeDiff);
120                         MediaPreviewTableRow row = new MediaPreviewTableRow(pair);
121                         // Don't try to correlate audios which don't have points either side
122                         boolean correlateAudio = pair.isValid();
123                         // Don't select audios which already have a point
124                         if (audio.getCurrentStatus() != AudioClip.Status.NOT_CONNECTED) {correlateAudio = false;}
125                         // Check time limits, distance limits
126                         if (timeLimit != null && correlateAudio) {
127                                 long numSecs = pair.getMinSeconds();
128                                 correlateAudio = (numSecs <= timeLimit.getTotalSeconds());
129                         }
130                         if (angDistLimit > 0.0 && correlateAudio)
131                         {
132                                 final double angDistPair = DataPoint.calculateRadiansBetween(pair.getPointBefore(), pair.getPointAfter());
133                                 double frac = pair.getFraction();
134                                 if (frac > 0.5) {frac = 1 - frac;}
135                                 final double angDistPhoto = angDistPair * frac;
136                                 correlateAudio = (angDistPhoto < angDistLimit);
137                         }
138                         // Don't select audios which are already correlated to the same point
139                         if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(audio.getDataPoint())) {
140                                 correlateAudio = false;
141                         }
142                         row.setCorrelateFlag(correlateAudio);
143                         model.addRow(row);
144                 }
145                 _previewTable.setModel(model);
146                 // Set distance units
147                 model.setDistanceUnits(getSelectedDistanceUnits());
148                 // Set column widths
149                 _previewTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
150                 final int[] colWidths = {150, 160, 100, 100, 50};
151                 for (int i=0; i<model.getColumnCount(); i++) {
152                         _previewTable.getColumnModel().getColumn(i).setPreferredWidth(colWidths[i]);
153                 }
154                 // check if any audios found
155                 _okButton.setEnabled(model.hasAnySelected());
156                 if (inShowWarning && !model.hasAnySelected())
157                 {
158                         JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.correlate.alloutsiderange"),
159                                 I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
160                 }
161         }
162
163         /**
164          * @return modified timestamp of specified media object
165          */
166         protected Timestamp getMediaTimestamp(MediaObject inMedia)
167         {
168                 Timestamp tstamp = super.getMediaTimestamp(inMedia);
169                 long mediaMillis = tstamp.getMilliseconds(TimezoneHelper.getSelectedTimezone());
170                 try {
171                         AudioClip audio = (AudioClip) inMedia;
172                         int audioLength = audio.getLengthInSeconds();
173                         // Each option is worth half the length of the audio clip, so need to divide by 2
174                         int secsToAdd = audioLength *
175                                 (_correlTimesSelector.getSelectedOption() - _fileTimesSelector.getSelectedOption()) / 2;
176                         if (audioLength > 0 && secsToAdd != 0)
177                         {
178                                 mediaMillis += (secsToAdd * 1000L);
179                                 tstamp = new TimestampUtc(mediaMillis);
180                                 // Here we create a Utc timestamp but it's only temporary for the correlation
181                                 // so it will never have to react to timezone changes
182                         }
183                 }
184                 catch (ClassCastException cce) {}
185                 return tstamp;
186         }
187
188         /**
189          * Finish the correlation by modifying the track
190          * and passing the Undo information back to the App
191          */
192         protected void finishCorrelation()
193         {
194                 // TODO: Probably should be able to combine this into the Correlator?
195                 PointMediaPair[] pointPairs = getPointPairs();
196                 if (pointPairs == null || pointPairs.length <= 0) {return;}
197
198                 // begin to construct undo information
199                 UndoCorrelateAudios undo = new UndoCorrelateAudios(_app.getTrackInfo());
200                 // loop over Audios
201                 int arraySize = pointPairs.length;
202                 int i = 0, numAudios = 0;
203                 int numPointsToCreate = 0;
204                 PointMediaPair pair = null;
205                 for (i=0; i<arraySize; i++)
206                 {
207                         pair = pointPairs[i];
208                         if (pair != null && pair.isValid())
209                         {
210                                 if (pair.getMinSeconds() == 0L)
211                                 {
212                                         // exact match
213                                         AudioClip pointAudio = pair.getPointBefore().getAudio();
214                                         if (pointAudio == null)
215                                         {
216                                                 // photo coincides with audioless point so connect the two
217                                                 pair.getPointBefore().setAudio((AudioClip) pair.getMedia());
218                                                 pair.getMedia().setDataPoint(pair.getPointBefore());
219                                         }
220                                         else if (pointAudio.equals(pair.getMedia())) {
221                                                 // photo is already connected, nothing to do
222                                         }
223                                         else {
224                                                 // point is already connected to a different audio, so need to clone point
225                                                 numPointsToCreate++;
226                                         }
227                                 }
228                                 else
229                                 {
230                                         // audio time falls between two points, so need to interpolate new one
231                                         numPointsToCreate++;
232                                 }
233                                 numAudios++;
234                         }
235                 }
236                 // Second loop, to create points if necessary
237                 if (numPointsToCreate > 0)
238                 {
239                         // make new array for added points
240                         DataPoint[] addedPoints = new DataPoint[numPointsToCreate];
241                         int pointNum = 0;
242                         DataPoint pointToAdd = null;
243                         for (i=0; i<arraySize; i++)
244                         {
245                                 pair = pointPairs[i];
246                                 if (pair != null && pair.isValid())
247                                 {
248                                         pointToAdd = null;
249                                         if (pair.getMinSeconds() == 0L && pair.getPointBefore().getAudio() != null
250                                          && !pair.getPointBefore().getAudio().equals(pair.getMedia()))
251                                         {
252                                                 // clone point
253                                                 pointToAdd = pair.getPointBefore().clonePoint();
254                                         }
255                                         else if (pair.getMinSeconds() > 0L)
256                                         {
257                                                 // interpolate point
258                                                 pointToAdd = DataPoint.interpolate(pair.getPointBefore(), pair.getPointAfter(), pair.getFraction());
259                                         }
260                                         if (pointToAdd != null)
261                                         {
262                                                 // link audio to point
263                                                 pointToAdd.setAudio((AudioClip) pair.getMedia());
264                                                 pair.getMedia().setDataPoint(pointToAdd);
265                                                 // set to start of segment so not joined in track
266                                                 pointToAdd.setSegmentStart(true);
267                                                 // add to point array
268                                                 addedPoints[pointNum] = pointToAdd;
269                                                 pointNum++;
270                                         }
271                                 }
272                         }
273                         // expand track
274                         _app.getTrackInfo().getTrack().appendPoints(addedPoints);
275                 }
276
277                 // send undo information back to controlling app
278                 undo.setNumAudiosCorrelated(numAudios);
279                 _app.completeFunction(undo, ("" + numAudios + " "
280                          + (numAudios==1?I18nManager.getText("confirm.correlateaudios.single"):I18nManager.getText("confirm.correlateaudios.multi"))));
281                 // observers already informed by track update if new points created
282                 if (numPointsToCreate == 0) {
283                         UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
284                 }
285         }
286 }