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