source: trunk/src_apollo/org/apollo/util/TrackModelLoadManager.java@ 315

Last change on this file since 315 was 315, checked in by bjn8, 16 years ago

Apollo spin-off added

File size: 5.4 KB
Line 
1package org.apollo.util;
2
3import java.io.File;
4import java.io.IOException;
5import java.lang.reflect.InvocationTargetException;
6import java.util.HashSet;
7import java.util.Set;
8
9import javax.sound.sampled.UnsupportedAudioFileException;
10import javax.swing.SwingUtilities;
11
12import org.apollo.audio.SampledTrackModel;
13import org.apollo.io.AudioIO;
14import org.apollo.io.LoadedAudioData;
15import org.apollo.mvc.Observer;
16import org.expeditee.items.widgets.HeavyDutyInteractiveWidget;
17import org.expeditee.items.widgets.WidgetCacheManager;
18
19/**
20 * Purpose: to avoid race conditions when loading track models from many threads.
21 *
22 * All sharing of track models is done here.
23 *
24 * Looked at disposed design patterns... but they become way to deep!
25 *
26 * @author Brook Novak
27 *
28 */
29public class TrackModelLoadManager {
30
31 private Set<TrackModelHandler> trackModelHandlers = new HashSet<TrackModelHandler>();
32
33 private static TrackModelLoadManager instance = new TrackModelLoadManager();
34
35 public static TrackModelLoadManager getInstance() {
36 return instance;
37 }
38
39 private TrackModelLoadManager() {
40 }
41
42
43 /**
44 * Don't go adding everything possible... only add a handler if the handlers lifetime is
45 * for the whole application, or is "disposable".
46 *
47 * @param handler
48 */
49 public void addTrackModelHandler(TrackModelHandler handler) {
50 assert(handler != null);
51 synchronized(trackModelHandlers) {
52 trackModelHandlers.add(handler);
53 }
54 }
55
56 /**
57 * Always remove to get rid of reference and keep memory finding routines fast
58 *
59 * @param handler
60 *
61 */
62 public void removeTrackModelHandler(TrackModelHandler handler) {
63 assert(handler != null);
64 synchronized(trackModelHandlers) {
65 trackModelHandlers.remove(handler);
66 }
67 }
68
69 /**
70 * Loads a SampledTrackModel. First it attempts to find is in memory, if fails then it
71 * will resort to loading from file.
72 *
73 * Note that if loaded from file and required conversions, the SampledTrackModel will
74 * be marked as being modified (requiring saving).
75 *
76 * @param filepath
77 * The filepath of the audio file to load. The SampledTrackModel will set its filename as
78 * this path value.
79 *
80 * @param checkExpediteeMemory
81 * If set to true, then it will search for SampledTrackModels in expeditee cache
82 * on the swing thread. Therefore MUST <b>NOT</b> BE ON THE SWINGTHREAD IF THIS IS SET TO TRUE.
83 *
84 * @param observer
85 * If given (can be null), then the observer will be notified with
86 * LOAD_STATUS_REPORT AudioSubjectChangedEvent events (on the calling thread).
87 * The state will be a float with the current percent (between 0.0 and 1.0 inclusive).
88 * The subject source will be this instance.
89 *
90 * @param localFilename
91 * The local filename to associate the loaded track with.
92 * Must not be null.
93 *
94 * @return
95 * Null iff cancelled.
96 *
97 * @throws IOException
98 * @throws UnsupportedAudioFileException
99 */
100 public synchronized SampledTrackModel load(
101 String filepath,
102 String localfilename,
103 Observer observer,
104 boolean checkExpediteeMemory) throws IOException, UnsupportedAudioFileException {
105
106 assert(localfilename != null);
107
108 // Check to see if already in memory
109 synchronized(trackModelHandlers) {
110 for (TrackModelHandler handler : trackModelHandlers) {
111 SampledTrackModel tm = handler.getSharedSampledTrackModel(localfilename);
112 if (tm != null) {
113 return tm;
114 }
115 }
116 }
117
118 if (checkExpediteeMemory) {
119 // MUST NOT BE ON SWING THREAD!
120 // THIS IS IMPORTANT FOR WHEN FRAMES HAVE BEEN CHECK AND THEY WHERE NOT LOADED AT
121 // THE TIME AND THUS CACHED WIDGETS NOT IN MEMORY, BUT LATER IS CACHED ...
122
123 CachedTrackModelLocator locator = new CachedTrackModelLocator(localfilename);
124
125 try {
126 SwingUtilities.invokeAndWait(locator);
127 } catch (InterruptedException e) {
128 e.printStackTrace();
129 } catch (InvocationTargetException e) {
130 e.printStackTrace();
131 }
132
133 if (locator.located != null) {
134 return locator.located;
135 }
136
137 }
138
139
140 // If cannot find in memory, then attempt to load from file.
141 LoadedAudioData loadedAudio = AudioIO.loadAudioFile(new File(filepath), observer);
142
143 // Could have been cancelled
144 if (loadedAudio == null) return null;
145
146 // Create the track
147 SampledTrackModel track = new SampledTrackModel(
148 loadedAudio.getAudioBytes(),
149 loadedAudio.getAudioFormat(),
150 localfilename);
151
152 track.setFilepath(filepath);
153
154 // Set as modified if the audio bytes had to be converted
155 if (loadedAudio.wasConverted()) {
156 track.setAudioModifiedFlag(true);
157 }
158
159 return track;
160
161 }
162
163
164
165 /**
166 * Look in expeditee memory - this should be quick - will hold up the user
167 */
168 private class CachedTrackModelLocator implements Runnable
169 {
170 private String localfileName;
171 private SampledTrackModel located = null;
172
173 CachedTrackModelLocator(String localfileName) {
174 assert(localfileName != null);
175 this.localfileName = localfileName;
176 }
177
178 public void run() {
179
180 // Exploit knowledge that all SampledTrack are Cached HDW
181 for (HeavyDutyInteractiveWidget hdw : WidgetCacheManager.getTransientWidgets()) {
182 if (hdw instanceof TrackModelHandler) {
183
184 SampledTrackModel strack = ((TrackModelHandler)hdw).getSharedSampledTrackModel(localfileName);
185
186 if (strack != null) {
187 assert(strack.getLocalFilename().equals(localfileName));
188
189 // found a match that is cahced
190 located = strack;
191 return;
192 }
193 }
194 }
195
196 }
197
198 }
199
200
201}
Note: See TracBrowser for help on using the repository browser.