/[svn]/jsampler/trunk/src/org/jsampler/CC.java
ViewVC logotype

Contents of /jsampler/trunk/src/org/jsampler/CC.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2288 - (show annotations) (download)
Wed Nov 23 21:19:44 2011 UTC (12 years, 5 months ago) by iliev
File size: 37997 byte(s)
* Added option to select a sampler engine in Add/Edit Instrument dialog
* Moved all Swing dependent code outside the JSampler core

1 /*
2 * JSampler - a java front-end for LinuxSampler
3 *
4 * Copyright (C) 2005-2011 Grigor Iliev <grigor@grigoriliev.com>
5 *
6 * This file is part of JSampler.
7 *
8 * JSampler is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * JSampler is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with JSampler; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 */
22
23 package org.jsampler;
24
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.InputStream;
29
30 import java.util.Timer;
31 import java.util.TimerTask;
32 import java.util.Vector;
33
34 import java.util.logging.Handler;
35 import java.util.logging.Level;
36 import java.util.logging.Logger;
37 import java.util.logging.SimpleFormatter;
38 import java.util.logging.StreamHandler;
39
40 import net.sf.juife.PDUtils;
41 import net.sf.juife.Task;
42 import net.sf.juife.TaskQueue;
43
44 import net.sf.juife.event.GenericEvent;
45 import net.sf.juife.event.GenericListener;
46 import net.sf.juife.event.TaskEvent;
47 import net.sf.juife.event.TaskListener;
48 import net.sf.juife.event.TaskQueueEvent;
49 import net.sf.juife.event.TaskQueueListener;
50
51 import org.jsampler.event.ListEvent;
52 import org.jsampler.event.ListListener;
53 import org.jsampler.event.OrchestraEvent;
54 import org.jsampler.event.OrchestraListener;
55
56 import org.jsampler.task.*;
57
58 import org.jsampler.view.JSChannelsPane;
59 import org.jsampler.view.JSMainFrame;
60 import org.jsampler.view.JSProgress;
61 import org.jsampler.view.JSViewConfig;
62
63 import org.linuxsampler.lscp.Client;
64 import org.linuxsampler.lscp.FxSend;
65
66 import org.linuxsampler.lscp.event.*;
67
68 import org.w3c.dom.Document;
69 import org.w3c.dom.Node;
70
71 import static org.jsampler.JSI18n.i18n;
72
73
74 /**
75 * This class serves as a 'Control Center' of the application.
76 * It also provides some fundamental routines and access to most used objects.
77 * @author Grigor Iliev
78 */
79 public class CC {
80 private static Handler handler;
81 private static FileOutputStream fos;
82
83 private static JSViewConfig viewConfig = null;
84 private static JSMainFrame mainFrame = null;
85 private static JSProgress progress = null;
86
87 private final static Client lsClient = new Client();
88
89 private static String jSamplerHome = null;
90
91 private final static TaskQueue taskQueue = new TaskQueue();
92 private final static Timer timer = new Timer();
93 private static TimerTask progressTimerTask = null;
94
95 private static int connectionFailureCount = 0;
96
97 static class ProgressTimerTask extends TimerTask {
98 public void run() {
99 PDUtils.runOnUiThread(new Runnable() { public void run() { run0(); } });
100 }
101
102 private void
103 run0() { CC.getProgressIndicator().start(); }
104 }
105
106 public static interface Run<A> {
107 public void run(A arg);
108 }
109
110 /** Forbids the instantiation of this class. */
111 private
112 CC() { }
113
114 /**
115 * Returns the logger to be used for logging events.
116 * @return The logger to be used for logging events.
117 */
118 public static Logger
119 getLogger() {
120 return Logger.getLogger (
121 "org.jsampler",
122 "org.jsampler.langprops.LogsBundle"
123 );
124 }
125
126 /**
127 * Returns the task queue to be used for scheduling tasks
128 * for execution out of the event-dispatching thread.
129 * @return The task queue to be used for scheduling tasks
130 * for execution out of the event-dispatching thread.
131 */
132 public static synchronized TaskQueue
133 getTaskQueue() { return taskQueue; }
134
135 /**
136 * Adds the specified task to the task queue. All task in the
137 * queue equal to the specified task are removed from the queue.
138 */
139 public static synchronized void
140 scheduleTask(Task t) {
141 while(getTaskQueue().removeTask(t)) { }
142
143 getTaskQueue().add(t);
144 }
145
146 /**
147 * Adds the specified task to the task queue only if the last
148 * task in the queue is not equal to <code>t</code>.
149 */
150 public static synchronized void
151 addTask(Task t) {
152 Task[] tasks = getTaskQueue().getPendingTasks();
153 if(tasks.length > 0 && tasks[tasks.length - 1].equals(t)) return;
154 getTaskQueue().add(t);
155 }
156
157 /**
158 * Gets the configuration of the current view.
159 */
160 public static JSViewConfig
161 getViewConfig() { return viewConfig; }
162
163 public static JSPrefs
164 preferences() { return getViewConfig().preferences(); }
165
166 /**
167 * Sets the configuration of the current view.
168 */
169 public static void
170 setViewConfig(JSViewConfig viewConfig) { CC.viewConfig = viewConfig; }
171
172 /**
173 * Returns the main window of this application.
174 * @return The main window of this application.
175 */
176 public static JSMainFrame<JSChannelsPane>
177 getMainFrame() { return mainFrame; }
178
179 /**
180 * Sets the main window of this application.
181 * @param mainFrame The main window of this application.
182 */
183 public static void
184 setMainFrame(JSMainFrame mainFrame) { CC.mainFrame = mainFrame; }
185
186 /**
187 * Gets the progress indicator of this application.
188 * @return The progress indicator of this application.
189 */
190 public static JSProgress
191 getProgressIndicator() { return progress; }
192
193 /**
194 * Sets the progress indicator to be used by this application.
195 * @param progress The progress indicator to be used by this application.
196 */
197 public static void
198 setProgressIndicator(JSProgress progress) { CC.progress = progress; }
199
200 /**
201 * Gets the absolute path to the JSampler's home location.
202 * @return The absolute path to the JSampler's home location
203 * or <code>null</code> if the JSampler's home location is not specified yet.
204 */
205 public static String
206 getJSamplerHome() { return jSamplerHome; }
207
208 /**
209 * Sets the location of the JSampler's home.
210 * @param path The new absolute path to the JSampler's home location.
211 */
212 public static void
213 setJSamplerHome(String path) {
214 jSamplerHome = path;
215 Prefs.setJSamplerHome(jSamplerHome);
216 }
217
218 /**
219 * This method does the initial preparation of the application.
220 */
221 protected static void
222 initJSampler() {
223 fos = null;
224 setJSamplerHome(Prefs.getJSamplerHome());
225 String s = getJSamplerHome();
226 try {
227 if(s != null) {
228 s += File.separator + "jsampler.log";
229 File f = new File(s);
230 if(f.isFile()) HF.createBackup("jsampler.log", "jsampler.log.0");
231 fos = new FileOutputStream(s);
232 }
233 } catch(Exception x) { x.printStackTrace(); }
234
235 if(fos == null) handler = new StreamHandler(System.out, new SimpleFormatter());
236 else handler = new StreamHandler(fos, new SimpleFormatter());
237
238 handler.setLevel(Level.FINE);
239 getLogger().addHandler(handler);
240 getLogger().setLevel(Level.FINE);
241 Logger.getLogger("org.linuxsampler.lscp").setLevel(Level.FINE);
242 Logger.getLogger("org.linuxsampler.lscp").addHandler(handler);
243
244 // Flushing logs on every second
245 timer.schedule(new java.util.TimerTask() {
246 public void
247 run() { if(handler != null) handler.flush(); }
248 }, 1000, 1000);
249
250 getLogger().fine("CC.jsStarted");
251
252 getTaskQueue().addTaskQueueListener(getHandler());
253
254 getTaskQueue().start();
255
256 getClient().removeChannelCountListener(getHandler());
257 getClient().addChannelCountListener(getHandler());
258
259 getClient().removeChannelInfoListener(getHandler());
260 getClient().addChannelInfoListener(getHandler());
261
262 getClient().removeFxSendCountListener(getHandler());
263 getClient().addFxSendCountListener(getHandler());
264
265 getClient().removeFxSendInfoListener(getHandler());
266 getClient().addFxSendInfoListener(getHandler());
267
268 getClient().removeStreamCountListener(getHandler());
269 getClient().addStreamCountListener(getHandler());
270
271 getClient().removeVoiceCountListener(getHandler());
272 getClient().addVoiceCountListener(getHandler());
273
274 getClient().removeTotalStreamCountListener(getHandler());
275 getClient().addTotalStreamCountListener(getHandler());
276
277 getClient().removeTotalVoiceCountListener(getHandler());
278 getClient().addTotalVoiceCountListener(getHandler());
279
280 getClient().removeAudioDeviceCountListener(audioDeviceCountListener);
281 getClient().addAudioDeviceCountListener(audioDeviceCountListener);
282
283 getClient().removeAudioDeviceInfoListener(audioDeviceInfoListener);
284 getClient().addAudioDeviceInfoListener(audioDeviceInfoListener);
285
286 getClient().removeMidiDeviceCountListener(midiDeviceCountListener);
287 getClient().addMidiDeviceCountListener(midiDeviceCountListener);
288
289 getClient().removeMidiDeviceInfoListener(midiDeviceInfoListener);
290 getClient().addMidiDeviceInfoListener(midiDeviceInfoListener);
291
292 getClient().removeMidiInstrumentMapCountListener(midiInstrMapCountListener);
293 getClient().addMidiInstrumentMapCountListener(midiInstrMapCountListener);
294
295 getClient().removeMidiInstrumentMapInfoListener(midiInstrMapInfoListener);
296 getClient().addMidiInstrumentMapInfoListener(midiInstrMapInfoListener);
297
298 getClient().removeMidiInstrumentCountListener(getHandler());
299 getClient().addMidiInstrumentCountListener(getHandler());
300
301 getClient().removeMidiInstrumentInfoListener(getHandler());
302 getClient().addMidiInstrumentInfoListener(getHandler());
303
304 getClient().removeGlobalInfoListener(getHandler());
305 getClient().addGlobalInfoListener(getHandler());
306
307 getClient().removeChannelMidiDataListener(getHandler());
308 getClient().addChannelMidiDataListener(getHandler());
309
310 getClient().removeSendEffectChainCountListener(getHandler());
311 getClient().addSendEffectChainCountListener(getHandler());
312
313 getClient().removeSendEffectChainInfoListener(getHandler());
314 getClient().addSendEffectChainInfoListener(getHandler());
315
316 getClient().removeEffectInstanceInfoListener(getHandler());
317 getClient().addEffectInstanceInfoListener(getHandler());
318
319 CC.addConnectionEstablishedListener(new GenericListener() {
320 public void
321 jobDone(GenericEvent e) {
322 connectionFailureCount = 0;
323 }
324 });
325 }
326
327 private final static OrchestraListModel orchestras = new DefaultOrchestraListModel();
328
329 /**
330 * Returns a list containing all available orchestras.
331 * @return A list containing all available orchestras.
332 */
333 public static OrchestraListModel
334 getOrchestras() { return orchestras; }
335
336 private final static ServerList servers = new ServerList();
337
338 /** Returns the server list. */
339 public static ServerList
340 getServerList() { return servers; }
341
342 private static ServerListListener serverListListener = new ServerListListener();
343
344 private static class ServerListListener implements GenericListener {
345 public void
346 jobDone(GenericEvent e) { saveServerList(); }
347 }
348
349 private static final
350
351 LostFilesModel lostFilesModel = new LostFilesModel();
352
353 public static LostFilesModel
354 getLostFilesModel() { return lostFilesModel; }
355
356 /**
357 * Loads the orchestras described in <code>&lt;jsampler_home&gt;/orchestras.xml</code>.
358 * If file with name <code>orchestras.xml.bkp</code> exist in the JSampler's home
359 * directory, this means that the last save has failed. In that case a recovery file
360 * <code>orchestras.xml.rec</code> is created and a recovery procedure
361 * will be initiated.
362 */
363 public static void
364 loadOrchestras() {
365 if(getJSamplerHome() == null) return;
366
367 try {
368 String s = getJSamplerHome();
369
370 File f = new File(s + File.separator + "orchestras.xml.bkp");
371 if(f.isFile()) HF.createBackup("orchestras.xml.bkp", "orchestras.xml.rec");
372
373 FileInputStream fis;
374 fis = new FileInputStream(s + File.separator + "orchestras.xml");
375
376 loadOrchestras(fis);
377 fis.close();
378 } catch(Exception x) {
379 getLogger().log(Level.INFO, HF.getErrorMessage(x), x);
380 }
381
382 getOrchestras().addOrchestraListListener(getHandler());
383 }
384
385
386 private static void
387 loadOrchestras(InputStream in) {
388 Document doc = DOMUtils.readObject(in);
389
390 try { getOrchestras().readObject(doc.getDocumentElement()); }
391 catch(Exception x) {
392 getViewConfig().showErrorMessage(x, "Loading orchestras: ");
393 return;
394 }
395
396 for(int i = 0; i < getOrchestras().getOrchestraCount(); i++) {
397 getOrchestras().getOrchestra(i).addOrchestraListener(getHandler());
398 }
399 }
400
401 private static void
402 saveOrchestras() {
403 try {
404 String s = getJSamplerHome();
405 if(s == null) return;
406
407 HF.createBackup("orchestras.xml", "orchestras.xml.bkp");
408
409 FileOutputStream fos2;
410 fos2 = new FileOutputStream(s + File.separator + "orchestras.xml", false);
411
412 Document doc = DOMUtils.createEmptyDocument();
413
414 Node node = doc.createElement("temp");
415 doc.appendChild(node);
416
417 getOrchestras().writeObject(doc, doc.getDocumentElement());
418
419 doc.replaceChild(node.getFirstChild(), node);
420
421 DOMUtils.writeObject(doc, fos2);
422
423 fos2.close();
424
425 HF.deleteFile("orchestras.xml.bkp");
426 } catch(Exception x) {
427 getViewConfig().showErrorMessage(x, "Saving orchestras: ");
428 return;
429 }
430 }
431
432 /**
433 * Loads the servers' info described in <code>&lt;jsampler_home&gt;/servers.xml</code>.
434 * If file with name <code>servers.xml.bkp</code> exist in the JSampler's home
435 * directory, this means that the last save has failed. In that case a recovery file
436 * <code>servers.xml.rec</code> is created and a recovery procedure
437 * will be initiated.
438 */
439 public static void
440 loadServerList() {
441 if(getJSamplerHome() == null) return;
442
443 try {
444 String s = getJSamplerHome();
445
446 File f = new File(s + File.separator + "servers.xml.bkp");
447 if(f.isFile()) HF.createBackup("servers.xml.bkp", "servers.xml.rec");
448
449 FileInputStream fis;
450 fis = new FileInputStream(s + File.separator + "servers.xml");
451
452 loadServerList(fis);
453 fis.close();
454 } catch(Exception x) {
455 getLogger().log(Level.INFO, HF.getErrorMessage(x), x);
456 }
457
458 getServerList().addChangeListener(serverListListener);
459
460 /* We should have at least one server to connect. */
461 if(getServerList().getServerCount() == 0) {
462 Server server = new Server();
463 server.setName("127.0.0.1:8888");
464 server.setAddress("127.0.0.1");
465 server.setPort(8888);
466 getServerList().addServer(server);
467 }
468 }
469
470
471 private static void
472 loadServerList(InputStream in) {
473 Document doc = DOMUtils.readObject(in);
474
475 try { getServerList().readObject(doc.getDocumentElement()); }
476 catch(Exception x) {
477 getViewConfig().showErrorMessage(x, "Loading server list: ");
478 return;
479 }
480 }
481
482 private static void
483 saveServerList() {
484 try {
485 String s = getJSamplerHome();
486 if(s == null) return;
487
488 HF.createBackup("servers.xml", "servers.xml.bkp");
489
490 FileOutputStream fos2;
491 fos2 = new FileOutputStream(s + File.separator + "servers.xml", false);
492
493 Document doc = DOMUtils.createEmptyDocument();
494
495 Node node = doc.createElement("temp");
496 doc.appendChild(node);
497
498 getServerList().writeObject(doc, doc.getDocumentElement());
499
500 doc.replaceChild(node.getFirstChild(), node);
501
502 DOMUtils.writeObject(doc, fos2);
503
504 fos2.close();
505
506 HF.deleteFile("servers.xml.bkp");
507 } catch(Exception x) {
508 getViewConfig().showErrorMessage(x, "Saving server list: ");
509 return;
510 }
511 }
512
513 /**
514 * The exit point of the application which ensures clean exit with default exit status 0.
515 * @see #cleanExit(int i)
516 */
517 public static void
518 cleanExit() { cleanExit(0); }
519
520 /**
521 * The exit point of the application which ensures clean exit.
522 * @param i The exit status.
523 */
524 public static void
525 cleanExit(int i) {
526 getLogger().fine("CC.jsEnded");
527 try { getClient().disconnect(); } // FIXME: this might block the EDT
528 catch(Exception x) { x.printStackTrace(); }
529 if(backendProcess != null) backendProcess.destroy();
530 backendProcess = null;
531 fireBackendProcessEvent();
532 System.exit(i);
533 }
534
535 /**
536 * Gets the <code>Client</code> object that is used to communicate with the backend.
537 * @return The <code>Client</code> object that is used to communicate with the backend.
538 */
539 public static Client
540 getClient() { return lsClient; }
541
542 private static final Vector<GenericListener> listeners = new Vector<GenericListener>();
543
544 /**
545 * Registers the specified listener to be notified (outside the UI thread)
546 * when reconnecting to LinuxSampler.
547 * @param l The <code>ActionListener</code> to register.
548 */
549 public static void
550 addReconnectListener(GenericListener l) { listeners.add(l); }
551
552 /**
553 * Removes the specified listener.
554 * @param l The <code>ActionListener</code> to remove.
555 */
556 public static void
557 removeReconnectListener(GenericListener l) { listeners.remove(l); }
558
559 private static void
560 fireReconnectEvent() {
561 GenericEvent e = new GenericEvent(CC.class);
562 for(GenericListener l : listeners) l.jobDone(e);
563 }
564
565 private static final Vector<GenericListener> ceListeners = new Vector<GenericListener>();
566
567 /**
568 * Registers the specified listener to be notified (from the UI thread) when
569 * jsampler is connected successfully to LinuxSampler.
570 * @param l The <code>GenericListener</code> to register.
571 */
572 public static void
573 addConnectionEstablishedListener(GenericListener l) { ceListeners.add(l); }
574
575 /**
576 * Removes the specified listener.
577 * @param l The <code>GenericListener</code> to remove.
578 */
579 public static void
580 removeConnectionEstablishedListener(GenericListener l) { ceListeners.remove(l); }
581
582 private static void
583 fireConnectionEstablishedEvent() {
584 GenericEvent e = new GenericEvent(CC.class);
585 for(GenericListener l : ceListeners) l.jobDone(e);
586 }
587
588 private static final SamplerModel samplerModel = new DefaultSamplerModel();
589
590 /**
591 * Gets the sampler model.
592 * @return The sampler model.
593 */
594 public static SamplerModel
595 getSamplerModel() { return samplerModel; }
596
597 /**
598 * Connects to LinuxSampler.
599 * The connection can be done asynchronously.
600 */
601 public static void
602 connect() { initSamplerModel(); }
603
604 /**
605 * Reconnects to LinuxSampler.
606 */
607 public static void
608 reconnect() { initSamplerModel(getCurrentServer()); }
609
610 private static Server currentServer = null;
611
612 /**
613 * Gets the server, to which the frontend is going to connect
614 * or is already connected.
615 */
616 public static Server
617 getCurrentServer() { return currentServer; }
618
619 /**
620 * Sets the current server.
621 */
622 public static void
623 setCurrentServer(Server server) {
624 if(server == currentServer) return;
625 connectionFailureCount = 0;
626 currentServer = server;
627 }
628
629 /**
630 * Sets the LSCP client's read timeout.
631 * @param timeout The new timeout value (in seconds).
632 */
633 public static void
634 setClientReadTimeout(int timeout) {
635 getTaskQueue().add(new Global.SetClientReadTimeout(timeout));
636 }
637
638 /**
639 * This method updates the information about the backend state.
640 * The update can be done asynchronously.
641 */
642 private static void
643 initSamplerModel() {
644 getMainFrame().getServer(new Run<Server>() {
645 public void
646 run(Server srv) {
647 if(srv == null) return;
648 initSamplerModel(srv);
649 }
650 });
651 }
652
653 /**
654 * This method updates the information about the backend state.
655 */
656 private static void
657 initSamplerModel(Server srv) {
658 setCurrentServer(srv);
659 final SetServerAddress ssa = new SetServerAddress(srv.getAddress(), srv.getPort());
660
661 final DefaultSamplerModel model = (DefaultSamplerModel)getSamplerModel();
662
663 final Global.GetServerInfo gsi = new Global.GetServerInfo();
664 gsi.addTaskListener(new TaskListener() {
665 public void
666 taskPerformed(TaskEvent e) {
667 if(gsi.doneWithErrors()) return;
668
669 model.setServerInfo(gsi.getResult());
670
671 if(CC.getViewConfig().getInstrumentsDbSupport()) {
672 CC.getViewConfig().initInstrumentsDbTreeModel();
673 }
674 }
675 });
676
677 final Audio.GetDrivers gaod = new Audio.GetDrivers();
678 gaod.addTaskListener(new TaskListener() {
679 public void
680 taskPerformed(TaskEvent e) {
681 if(!gaod.doneWithErrors())
682 model.setAudioOutputDrivers(gaod.getResult());
683 }
684 });
685
686 final Global.GetEngines ge = new Global.GetEngines();
687 ge.addTaskListener(new TaskListener() {
688 public void
689 taskPerformed(TaskEvent e) {
690 if(!ge.doneWithErrors()) model.setEngines(ge.getResult());
691 }
692 });
693
694 final Midi.GetDrivers gmid = new Midi.GetDrivers();
695 gmid.addTaskListener(new TaskListener() {
696 public void
697 taskPerformed(TaskEvent e) {
698 if(!gmid.doneWithErrors())
699 model.setMidiInputDrivers(gmid.getResult());
700 }
701 });
702
703 final Global.GetVolume gv = new Global.GetVolume();
704 gv.addTaskListener(new TaskListener() {
705 public void
706 taskPerformed(TaskEvent e) {
707 if(!gv.doneWithErrors())
708 model.setVolume(gv.getResult());
709 }
710 });
711
712 final Midi.GetInstrumentMaps mgim = new Midi.GetInstrumentMaps();
713 mgim.addTaskListener(new TaskListener() {
714 public void
715 taskPerformed(TaskEvent e) {
716 if(mgim.doneWithErrors()) return;
717 model.removeAllMidiInstrumentMaps();
718
719 for(MidiInstrumentMap map : mgim.getResult()) {
720 model.addMidiInstrumentMap(map);
721 }
722 }
723 });
724
725 final Global.GetEffects gfx = new Global.GetEffects();
726 gfx.addTaskListener(new TaskListener() {
727 public void
728 taskPerformed(TaskEvent e) {
729 if(!gfx.doneWithErrors()) model.getEffects().setEffects(gfx.getResult());
730 }
731 });
732
733 final UpdateChannels uc = new UpdateChannels();
734 uc.addTaskListener(new TaskListener() {
735 public void
736 taskPerformed(TaskEvent e) {
737 for(SamplerChannelModel c : model.getChannels()) {
738 if(c.getChannelInfo().getEngine() == null) continue;
739
740 Channel.GetFxSends gfs = new Channel.GetFxSends();
741 gfs.setChannel(c.getChannelId());
742 gfs.addTaskListener(new GetFxSendsListener());
743 getTaskQueue().add(gfs);
744 }
745
746 // TODO: This should be done after the fx sends are set
747 //CC.getSamplerModel().setModified(false);
748 }
749 });
750
751
752 final Global.Connect cnt = new Global.Connect();
753 boolean b = preferences().getBoolProperty(JSPrefs.LAUNCH_BACKEND_LOCALLY);
754 if(b && srv.isLocal() && backendProcess == null) cnt.setSilent(true);
755 cnt.addTaskListener(new TaskListener() {
756 public void
757 taskPerformed(TaskEvent e) {
758 if(cnt.doneWithErrors()) {
759 onConnectFailure();
760 return;
761 }
762
763 // Don't change order!!!
764 getTaskQueue().add(gsi);
765 getTaskQueue().add(gaod);
766 getTaskQueue().add(gmid);
767 getTaskQueue().add(ge);
768 getTaskQueue().add(gv);
769 getTaskQueue().add(mgim);
770 getTaskQueue().add(gfx);
771 getTaskQueue().add(new Midi.UpdateDevices());
772 getTaskQueue().add(new Audio.UpdateDevices());
773 addTask(uc);
774 getTaskQueue().add(new Global.UpdateSendEffectChains());
775
776 int vl = preferences().getIntProperty(JSPrefs.GLOBAL_VOICE_LIMIT);
777 int sl = preferences().getIntProperty(JSPrefs.GLOBAL_STREAM_LIMIT);
778
779 getTaskQueue().add(new Global.SetPolyphony(vl, sl));
780
781 fireConnectionEstablishedEvent();
782 }
783 });
784
785 ssa.addTaskListener(new TaskListener() {
786 public void
787 taskPerformed(TaskEvent e) {
788 int t = preferences().getIntProperty(JSPrefs.SOCKET_READ_TIMEOUT);
789 CC.setClientReadTimeout(t * 1000);
790 CC.getTaskQueue().add(cnt);
791 }
792 });
793
794 getSamplerModel().reset();
795 getViewConfig().resetInstrumentsDbTreeModel();
796
797 getTaskQueue().removePendingTasks();
798 getTaskQueue().add(ssa);
799
800 fireReconnectEvent();
801 }
802
803 private static void
804 onConnectFailure() {
805 connectionFailureCount++;
806 if(connectionFailureCount > 50) { // to prevent eventual infinite loop
807 getLogger().warning("Reached maximum number of connection failures");
808 return;
809 }
810
811 try {
812 if(launchBackend()) {
813 int i = preferences().getIntProperty(JSPrefs.BACKEND_LAUNCH_DELAY);
814 if(i < 1) {
815 initSamplerModel(getCurrentServer());
816 return;
817 }
818
819 LaunchBackend lb = new LaunchBackend(i, getBackendMonitor());
820 //CC.getTaskQueue().add(lb);
821 new Thread(lb).start();
822 return;
823 }
824 } catch(Exception x) {
825 final String s = JSI18n.i18n.getError("CC.failedToLaunchBackend");
826 CC.getLogger().log(Level.INFO, s, x);
827
828 PDUtils.runOnUiThread(new Runnable() {
829 public void
830 run() { getViewConfig().showErrorMessage(s); }
831 });
832 return;
833 }
834
835 retryToConnect();
836 }
837
838 private static void
839 retryToConnect() {
840 PDUtils.runOnUiThread(new Runnable() {
841 public void
842 run() { changeBackend(); }
843 });
844 }
845
846 public static void
847 changeBackend() {
848 getMainFrame().getServer(new Run<Server>() {
849 public void
850 run(Server srv) {
851 if(srv == null) return;
852 connectionFailureCount = 0; // cleared because this change is due to user interaction
853 initSamplerModel(srv);
854 }
855 }, true);
856 }
857
858 private static final Vector<GenericListener> pListeners = new Vector<GenericListener>();
859
860 /**
861 * Registers the specified listener to be notified (from UI thread) when
862 * backend process is created/terminated.
863 * @param l The <code>ActionListener</code> to register.
864 */
865 public static void
866 addBackendProcessListener(GenericListener l) { pListeners.add(l); }
867
868 /**
869 * Removes the specified listener.
870 * @param l The <code>ActionListener</code> to remove.
871 */
872 public static void
873 removeBackendProcessListener(GenericListener l) { pListeners.remove(l); }
874
875 private static void
876 fireBackendProcessEvent() {
877 GenericEvent e = new GenericEvent(CC.class);
878 for(GenericListener l : pListeners) l.jobDone(e);
879 }
880
881 private static Process backendProcess = null;
882
883 public static Process
884 getBackendProcess() { return backendProcess; }
885
886 private static final Object backendMonitor = new Object();
887
888 public static Object
889 getBackendMonitor() { return backendMonitor; }
890
891 private static boolean
892 launchBackend() throws Exception {
893 if(backendProcess != null) {
894 try {
895 int i = backendProcess.exitValue();
896 getLogger().info("Backend exited with exit value " + i);
897 backendProcess = null;
898 fireBackendProcessEvent();
899 } catch(IllegalThreadStateException x) { return false; }
900 }
901
902 if(!preferences().getBoolProperty(JSPrefs.LAUNCH_BACKEND_LOCALLY)) return false;
903 if(connectionFailureCount > 1) return false;
904
905 Server s = getCurrentServer();
906 if(s != null && s.isLocal()) {
907 String cmd = preferences().getStringProperty(JSPrefs.BACKEND_LAUNCH_COMMAND);
908 backendProcess = Runtime.getRuntime().exec(cmd);
909 fireBackendProcessEvent();
910 return true;
911 }
912
913 return false;
914 }
915
916 private static class GetFxSendsListener implements TaskListener {
917 public void
918 taskPerformed(TaskEvent e) {
919 Channel.GetFxSends gfs = (Channel.GetFxSends)e.getSource();
920 if(gfs.doneWithErrors()) return;
921 SamplerChannelModel m = getSamplerModel().getChannelById(gfs.getChannel());
922 m.removeAllFxSends();
923
924 for(FxSend fxs : gfs.getResult()) m.addFxSend(fxs);
925 }
926 }
927
928 public static void
929 scheduleInTaskQueue(final Runnable r) {
930 Task dummy = new Global.DummyTask();
931 dummy.addTaskListener(new TaskListener() {
932 public void
933 taskPerformed(TaskEvent e) {
934 PDUtils.runOnUiThread(r);
935 }
936 });
937
938 getTaskQueue().add(dummy);
939 }
940
941 public static boolean
942 verifyConnection() {
943 if(getCurrentServer() == null) {
944 getViewConfig().showErrorMessage(i18n.getError("CC.notConnected"));
945 return false;
946 }
947
948 return true;
949 }
950
951 public static boolean
952 isMacOS() {
953 return System.getProperty("os.name").toLowerCase().startsWith("mac os x");
954 }
955
956
957 private final static EventHandler eventHandler = new EventHandler();
958
959 private static EventHandler
960 getHandler() { return eventHandler; }
961
962 private static class EventHandler implements ChannelCountListener, ChannelInfoListener,
963 FxSendCountListener, FxSendInfoListener, StreamCountListener, VoiceCountListener,
964 TotalStreamCountListener, TotalVoiceCountListener, TaskQueueListener,
965 OrchestraListener, ListListener<OrchestraModel>, MidiInstrumentCountListener,
966 MidiInstrumentInfoListener, GlobalInfoListener, ChannelMidiDataListener,
967 SendEffectChainCountListener, SendEffectChainInfoListener, EffectInstanceInfoListener {
968
969 /** Invoked when the number of channels has changed. */
970 @Override
971 public void
972 channelCountChanged( ChannelCountEvent e) {
973 if(e.getChannelCount() == 0) {
974 /*
975 * This special case is handled because this might be due to
976 * loading a lscp script containing sampler view configuration.
977 */
978 CC.getSamplerModel().removeAllChannels();
979 return;
980 }
981 addTask(new UpdateChannels());
982 }
983
984 /** Invoked when changes to the sampler channel has occured. */
985 @Override
986 public void
987 channelInfoChanged(ChannelInfoEvent e) {
988 /*
989 * Because of the rapid notification flow when instrument is loaded
990 * we need to do some optimization to decrease the traffic.
991 */
992 boolean b = true;
993 Task[] tS = getTaskQueue().getPendingTasks();
994
995 for(int i = tS.length - 1; i >= 0; i--) {
996 Task t = tS[i];
997
998 if(t instanceof Channel.UpdateInfo) {
999 Channel.UpdateInfo cui = (Channel.UpdateInfo)t;
1000 if(cui.getChannelId() == e.getSamplerChannel()) return;
1001 } else {
1002 b = false;
1003 break;
1004 }
1005 }
1006
1007 if(b) {
1008 Task t = getTaskQueue().getRunningTask();
1009 if(t instanceof Channel.UpdateInfo) {
1010 Channel.UpdateInfo cui = (Channel.UpdateInfo)t;
1011 if(cui.getChannelId() == e.getSamplerChannel()) return;
1012 }
1013 }
1014
1015
1016 getTaskQueue().add(new Channel.UpdateInfo(e.getSamplerChannel()));
1017 }
1018
1019 /**
1020 * Invoked when the number of effect sends
1021 * on a particular sampler channel has changed.
1022 */
1023 @Override
1024 public void
1025 fxSendCountChanged(FxSendCountEvent e) {
1026 getTaskQueue().add(new Channel.UpdateFxSends(e.getChannel()));
1027 }
1028
1029 /**
1030 * Invoked when the settings of an effect sends are changed.
1031 */
1032 @Override
1033 public void
1034 fxSendInfoChanged(FxSendInfoEvent e) {
1035 Task t = new Channel.UpdateFxSendInfo(e.getChannel(), e.getFxSend());
1036 getTaskQueue().add(t);
1037 }
1038
1039 /**
1040 * Invoked when the number of active disk
1041 * streams in a specific sampler channel has changed.
1042 */
1043 @Override
1044 public void
1045 streamCountChanged(StreamCountEvent e) {
1046 SamplerChannelModel scm =
1047 getSamplerModel().getChannelById(e.getSamplerChannel());
1048
1049 if(scm == null) {
1050 CC.getLogger().log (
1051 Level.WARNING,
1052 "CC.unknownChannel!",
1053 e.getSamplerChannel()
1054 );
1055
1056 return;
1057 }
1058
1059 scm.setStreamCount(e.getStreamCount());
1060 }
1061
1062 /**
1063 * Invoked when the number of active voices
1064 * in a specific sampler channel has changed.
1065 */
1066 @Override
1067 public void
1068 voiceCountChanged(VoiceCountEvent e) {
1069 SamplerChannelModel scm =
1070 getSamplerModel().getChannelById(e.getSamplerChannel());
1071
1072 if(scm == null) {
1073 CC.getLogger().log (
1074 Level.WARNING,
1075 "CC.unknownChannel!",
1076 e.getSamplerChannel()
1077 );
1078
1079 return;
1080 }
1081
1082 scm.setVoiceCount(e.getVoiceCount());
1083 }
1084
1085 /** Invoked when the total number of active streams has changed. */
1086 @Override
1087 public void
1088 totalStreamCountChanged(TotalStreamCountEvent e) {
1089 getSamplerModel().updateActiveStreamsInfo(e.getTotalStreamCount());
1090 }
1091
1092 /** Invoked when the total number of active voices has changed. */
1093 @Override
1094 public void
1095 totalVoiceCountChanged(TotalVoiceCountEvent e) {
1096 scheduleTask(new Global.UpdateTotalVoiceCount());
1097 }
1098
1099 /** Invoked when the number of MIDI instruments in a MIDI instrument map is changed. */
1100 @Override
1101 public void
1102 instrumentCountChanged(MidiInstrumentCountEvent e) {
1103 scheduleTask(new Midi.UpdateInstruments(e.getMapId()));
1104 }
1105
1106 /** Invoked when a MIDI instrument in a MIDI instrument map is changed. */
1107 @Override
1108 public void
1109 instrumentInfoChanged(MidiInstrumentInfoEvent e) {
1110 Task t = new Midi.UpdateInstrumentInfo (
1111 e.getMapId(), e.getMidiBank(), e.getMidiProgram()
1112 );
1113 getTaskQueue().add(t);
1114
1115 }
1116
1117 /** Invoked when the global volume of the sampler is changed. */
1118 @Override
1119 public void
1120 volumeChanged(GlobalInfoEvent e) {
1121 getSamplerModel().setVolume(e.getVolume());
1122 }
1123
1124 @Override
1125 public void
1126 voiceLimitChanged(GlobalInfoEvent e) { }
1127
1128 @Override
1129 public void
1130 streamLimitChanged(GlobalInfoEvent e) { }
1131
1132 /**
1133 * Invoked to indicate that the state of a task queue is changed.
1134 * This method is invoked only from the UI thread.
1135 */
1136 @Override
1137 public void
1138 stateChanged(TaskQueueEvent e) {
1139 switch(e.getEventID()) {
1140 case TASK_FETCHED:
1141 getProgressIndicator().setString (
1142 ((Task)e.getSource()).getDescription()
1143 );
1144 break;
1145 case TASK_DONE:
1146 EnhancedTask t = (EnhancedTask)e.getSource();
1147 if(t.doneWithErrors() && !t.isSilent()) {
1148 if(t.getErrorCode() == t.SOCKET_ERROR) {
1149 getMainFrame().handleConnectionFailure();
1150 } else if(!t.isStopped()) {
1151 showError(t);
1152 }
1153 }
1154 break;
1155 case NOT_IDLE:
1156 if(progressTimerTask != null) {
1157 getLogger().warning("progressTimerTask != null - this is a bug!");
1158 }
1159 progressTimerTask = new ProgressTimerTask();
1160 timer.schedule(progressTimerTask, 2000);
1161 break;
1162 case IDLE:
1163 if(progressTimerTask != null) {
1164 progressTimerTask.cancel();
1165 progressTimerTask = null;
1166 }
1167 getProgressIndicator().stop();
1168 break;
1169 }
1170 }
1171
1172 private void
1173 showError(final Task t) {
1174 PDUtils.runOnUiThread(new Runnable() {
1175 public void
1176 run() {
1177 if(t.getErrorDetails() == null) {
1178 getViewConfig().showErrorMessage(t.getErrorMessage());
1179 } else {
1180 getMainFrame().showDetailedErrorMessage (
1181 t.getErrorMessage(),
1182 t.getErrorDetails()
1183 );
1184 }
1185 }
1186 });
1187 }
1188
1189 /** Invoked when the name of orchestra is changed. */
1190 @Override
1191 public void
1192 nameChanged(OrchestraEvent e) { saveOrchestras(); }
1193
1194 /** Invoked when the description of orchestra is changed. */
1195 @Override
1196 public void
1197 descriptionChanged(OrchestraEvent e) { saveOrchestras(); }
1198
1199 /** Invoked when an instrument is added to the orchestra. */
1200 @Override
1201 public void
1202 instrumentAdded(OrchestraEvent e) { saveOrchestras(); }
1203
1204 /** Invoked when an instrument is removed from the orchestra. */
1205 @Override
1206 public void
1207 instrumentRemoved(OrchestraEvent e) { saveOrchestras(); }
1208
1209 /** Invoked when the settings of an instrument are changed. */
1210 @Override
1211 public void
1212 instrumentChanged(OrchestraEvent e) { saveOrchestras(); }
1213
1214 /** Invoked when an orchestra is added to the orchestra list. */
1215 @Override
1216 public void
1217 entryAdded(ListEvent<OrchestraModel> e) {
1218 e.getEntry().addOrchestraListener(getHandler());
1219 saveOrchestras();
1220 }
1221
1222 /** Invoked when an orchestra is removed from the orchestra list. */
1223 @Override
1224 public void
1225 entryRemoved(ListEvent<OrchestraModel> e) {
1226 e.getEntry().removeOrchestraListener(getHandler());
1227 saveOrchestras();
1228 }
1229
1230 /**
1231 * Invoked when MIDI data arrives.
1232 */
1233 @Override
1234 public void
1235 midiDataArrived(final ChannelMidiDataEvent e) {
1236 try {
1237 PDUtils.runOnUiThreadAndWait(new Runnable() {
1238 public void
1239 run() { fireChannelMidiDataEvent(e); }
1240 });
1241 } catch(Exception x) {
1242 CC.getLogger().log(Level.INFO, "Failed!", x);
1243 }
1244 }
1245
1246 @Override
1247 public void
1248 sendEffectChainCountChanged(SendEffectChainCountEvent e) {
1249 getTaskQueue().add(new Audio.UpdateSendEffectChains(e.getAudioDeviceId()));
1250 }
1251
1252 @Override
1253 public void
1254 sendEffectChainInfoChanged(SendEffectChainInfoEvent e) {
1255 if(e.getInstanceCount() == -1) return;
1256
1257 getTaskQueue().add (
1258 new Audio.UpdateEffectInstances(e.getAudioDeviceId(), e.getChainId())
1259 );
1260 }
1261
1262 @Override
1263 public void
1264 effectInstanceInfoChanged(EffectInstanceInfoEvent e) {
1265 getTaskQueue().add(new Audio.UpdateEffectInstanceInfo(e.getEffectInstanceId()));
1266 }
1267 }
1268
1269 private static void
1270 fireChannelMidiDataEvent(ChannelMidiDataEvent e) {
1271 SamplerChannelModel chn;
1272 chn = getSamplerModel().getChannelById(e.getChannelId());
1273 if(chn == null) {
1274 CC.getLogger().info("Unknown channel ID: " + e.getChannelId());
1275 }
1276
1277 ((DefaultSamplerChannelModel)chn).fireMidiDataEvent(e);
1278 }
1279
1280 private static final AudioDeviceCountListener audioDeviceCountListener =
1281 new AudioDeviceCountListener();
1282
1283 private static class AudioDeviceCountListener implements ItemCountListener {
1284 /** Invoked when the number of audio output devices has changed. */
1285 @Override
1286 public void
1287 itemCountChanged(ItemCountEvent e) {
1288 getTaskQueue().add(new Audio.UpdateDevices());
1289 }
1290 }
1291
1292 private static final AudioDeviceInfoListener audioDeviceInfoListener =
1293 new AudioDeviceInfoListener();
1294
1295 private static class AudioDeviceInfoListener implements ItemInfoListener {
1296 /** Invoked when the audio output device's settings are changed. */
1297 @Override
1298 public void
1299 itemInfoChanged(ItemInfoEvent e) {
1300 getTaskQueue().add(new Audio.UpdateDeviceInfo(e.getItemID()));
1301 }
1302 }
1303
1304 private static final MidiDeviceCountListener midiDeviceCountListener =
1305 new MidiDeviceCountListener();
1306
1307 private static class MidiDeviceCountListener implements ItemCountListener {
1308 /** Invoked when the number of MIDI input devices has changed. */
1309 public void
1310 itemCountChanged(ItemCountEvent e) {
1311 getTaskQueue().add(new Midi.UpdateDevices());
1312 }
1313 }
1314
1315 private static final MidiDeviceInfoListener midiDeviceInfoListener =
1316 new MidiDeviceInfoListener();
1317
1318 private static class MidiDeviceInfoListener implements ItemInfoListener {
1319 /** Invoked when the MIDI input device's settings are changed. */
1320 @Override
1321 public void
1322 itemInfoChanged(ItemInfoEvent e) {
1323 getTaskQueue().add(new Midi.UpdateDeviceInfo(e.getItemID()));
1324 }
1325 }
1326
1327 private static final MidiInstrMapCountListener midiInstrMapCountListener =
1328 new MidiInstrMapCountListener();
1329
1330 private static class MidiInstrMapCountListener implements ItemCountListener {
1331 /** Invoked when the number of MIDI instrument maps is changed. */
1332 @Override
1333 public void
1334 itemCountChanged(ItemCountEvent e) {
1335 getTaskQueue().add(new Midi.UpdateInstrumentMaps());
1336 }
1337 }
1338
1339 private static final MidiInstrMapInfoListener midiInstrMapInfoListener =
1340 new MidiInstrMapInfoListener();
1341
1342 private static class MidiInstrMapInfoListener implements ItemInfoListener {
1343 /** Invoked when the MIDI instrument map's settings are changed. */
1344 @Override
1345 public void
1346 itemInfoChanged(ItemInfoEvent e) {
1347 getTaskQueue().add(new Midi.UpdateInstrumentMapInfo(e.getItemID()));
1348 }
1349 }
1350 }

  ViewVC Help
Powered by ViewVC