1 package tim.prune.correlate;
3 import java.awt.FlowLayout;
4 import java.awt.GridLayout;
6 import javax.swing.JOptionPane;
7 import javax.swing.JPanel;
8 import javax.swing.JTable;
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;
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
27 public class AudioCorrelator extends Correlator
29 private AudioTimestampSelector _fileTimesSelector = null, _correlTimesSelector = null;
34 * @param inApp App object
36 public AudioCorrelator(App inApp) {
43 public String getNameKey() {
44 return "function.correlateaudios";
47 /** @return type key */
48 protected String getMediaTypeKey() {
52 /** @return photo list*/
53 protected MediaList getMediaList() {
54 return _app.getTrackInfo().getAudioList();
59 * @return first gui panel including timestamp specification (beginning, middle, end)
61 protected JPanel makeFirstPanel()
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);
78 * @return array of boolean flags denoting availability of cards
80 protected boolean[] getCardEnabledFlags()
82 boolean[] cards = super.getCardEnabledFlags();
83 cards[0] = getAudioLengthAvailability(_app.getTrackInfo().getAudioList());
88 * @param inAudios AudioList object
89 * @return true if there are any audio lengths available
91 private static boolean getAudioLengthAvailability(AudioList inAudios)
93 for (int i=0; i<inAudios.getNumMedia(); i++)
95 AudioClip a = inAudios.getAudio(i);
96 if (a.getLengthInSeconds() > 0) {return true;}
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
106 protected void createPreview(TimeDifference inTimeDiff, boolean inShowWarning)
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++)
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());
128 if (angDistLimit > 0.0 && correlateAudio)
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);
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;
140 row.setCorrelateFlag(correlateAudio);
143 _previewTable.setModel(model);
144 // Set distance units
145 model.setDistanceUnits(getSelectedDistanceUnits());
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]);
152 // check if any audios found
153 _okButton.setEnabled(model.hasAnySelected());
154 if (inShowWarning && !model.hasAnySelected())
156 JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.correlate.alloutsiderange"),
157 I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
162 * @return modified timestamp of specified media object
164 protected Timestamp getMediaTimestamp(MediaObject inMedia)
166 Timestamp tstamp = super.getMediaTimestamp(inMedia);
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);
177 catch (ClassCastException cce) {}
182 * Finish the correlation by modifying the track
183 * and passing the Undo information back to the App
185 protected void finishCorrelation()
187 // TODO: Probably should be able to combine this into the Correlator?
188 PointMediaPair[] pointPairs = getPointPairs();
189 if (pointPairs == null || pointPairs.length <= 0) {return;}
191 // begin to construct undo information
192 UndoCorrelateAudios undo = new UndoCorrelateAudios(_app.getTrackInfo());
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++)
200 pair = pointPairs[i];
201 if (pair != null && pair.isValid())
203 if (pair.getMinSeconds() == 0L)
206 AudioClip pointAudio = pair.getPointBefore().getAudio();
207 if (pointAudio == null)
209 // photo coincides with audioless point so connect the two
210 pair.getPointBefore().setAudio((AudioClip) pair.getMedia());
211 pair.getMedia().setDataPoint(pair.getPointBefore());
213 else if (pointAudio.equals(pair.getMedia())) {
214 // photo is already connected, nothing to do
217 // point is already connected to a different audio, so need to clone point
223 // audio time falls between two points, so need to interpolate new one
229 // Second loop, to create points if necessary
230 if (numPointsToCreate > 0)
232 // make new array for added points
233 DataPoint[] addedPoints = new DataPoint[numPointsToCreate];
235 DataPoint pointToAdd = null;
236 for (i=0; i<arraySize; i++)
238 pair = pointPairs[i];
239 if (pair != null && pair.isValid())
242 if (pair.getMinSeconds() == 0L && pair.getPointBefore().getAudio() != null
243 && !pair.getPointBefore().getAudio().equals(pair.getMedia()))
246 pointToAdd = pair.getPointBefore().clonePoint();
248 else if (pair.getMinSeconds() > 0L)
251 pointToAdd = DataPoint.interpolate(pair.getPointBefore(), pair.getPointAfter(), pair.getFraction());
253 if (pointToAdd != null)
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;
267 _app.getTrackInfo().getTrack().appendPoints(addedPoints);
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);