/[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 1915 - (show annotations) (download)
Thu Jun 11 09:35:29 2009 UTC (14 years, 10 months ago) by iliev
File size: 37374 byte(s)
* added support for exporting the MIDI instrument maps
  as text file or web page

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

  ViewVC Help
Powered by ViewVC