--- jsampler/trunk/src/org/jsampler/CC.java 2007/09/07 12:09:59 1327 +++ jsampler/trunk/src/org/jsampler/CC.java 2009/03/16 22:12:32 1867 @@ -1,7 +1,7 @@ /* * JSampler - a java front-end for LinuxSampler * - * Copyright (C) 2005-2007 Grigor Iliev + * Copyright (C) 2005-2009 Grigor Iliev * * This file is part of JSampler. * @@ -25,7 +25,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; @@ -40,8 +39,12 @@ import java.util.logging.SimpleFormatter; import java.util.logging.StreamHandler; +import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + import net.sf.juife.Task; import net.sf.juife.TaskQueue; @@ -99,6 +102,8 @@ private final static TaskQueue taskQueue = new TaskQueue(); private final static Timer timer = new Timer(2000, null); + private static int connectionFailureCount = 0; + /** Forbits the instantiation of this class. */ private CC() { } @@ -121,30 +126,40 @@ * @return The task queue to be used for scheduling tasks * for execution out of the event-dispatching thread. */ - public static TaskQueue + public static synchronized TaskQueue getTaskQueue() { return taskQueue; } /** * Adds the specified task to the task queue. All task in the * queue equal to the specified task are removed from the queue. */ - public static void + public static synchronized void scheduleTask(Task t) { while(getTaskQueue().removeTask(t)) { } - if(getTaskQueue().getPendingTaskCount() == 0) { - if(t.equals(getTaskQueue().getRunningTask())) return; - } - getTaskQueue().add(t); } /** + * Adds the specified task to the task queue only if the last + * task in the queue is not equal to t. + */ + public static synchronized void + addTask(Task t) { + Task[] tasks = getTaskQueue().getPendingTasks(); + if(tasks.length > 0 && tasks[tasks.length - 1].equals(t)) return; + getTaskQueue().add(t); + } + + /** * Gets the configuration of the current view. */ public static JSViewConfig getViewConfig() { return viewConfig; } + public static JSPrefs + preferences() { return getViewConfig().preferences(); } + /** * Sets the configuration of the current view. */ @@ -229,13 +244,10 @@ run() { if(handler != null) handler.flush(); } }, 1000, 1000); - CC.getLogger().fine("CC.jsStarted"); + getLogger().fine("CC.jsStarted"); HF.setUIDefaultFont(Prefs.getInterfaceFont()); - getClient().setServerAddress(Prefs.getLSAddress()); - getClient().setServerPort(Prefs.getLSPort()); - timer.setRepeats(false); timer.addActionListener(new ActionListener() { @@ -243,9 +255,9 @@ actionPerformed(ActionEvent e) { CC.getProgressIndicator().start(); } }); - taskQueue.addTaskQueueListener(getHandler()); + getTaskQueue().addTaskQueueListener(getHandler()); - taskQueue.start(); + getTaskQueue().start(); getClient().removeChannelCountListener(getHandler()); getClient().addChannelCountListener(getHandler()); @@ -265,6 +277,9 @@ getClient().removeVoiceCountListener(getHandler()); getClient().addVoiceCountListener(getHandler()); + getClient().removeTotalStreamCountListener(getHandler()); + getClient().addTotalStreamCountListener(getHandler()); + getClient().removeTotalVoiceCountListener(getHandler()); getClient().addTotalVoiceCountListener(getHandler()); @@ -294,6 +309,16 @@ getClient().removeGlobalInfoListener(getHandler()); getClient().addGlobalInfoListener(getHandler()); + + getClient().removeChannelMidiDataListener(getHandler()); + getClient().addChannelMidiDataListener(getHandler()); + + CC.addConnectionEstablishedListener(new ActionListener() { + public void + actionPerformed(ActionEvent e) { + connectionFailureCount = 0; + } + }); } /** @@ -312,7 +337,7 @@ } } - CC.getMainFrame().installJSamplerHome(); + getMainFrame().installJSamplerHome(); } /** @@ -369,7 +394,25 @@ public static OrchestraListModel getOrchestras() { return orchestras; } + private final static ServerList servers = new ServerList(); + + /** Returns the server list. */ + public static ServerList + getServerList() { return servers; } + + private static ServerListListener serverListListener = new ServerListListener(); + + private static class ServerListListener implements ChangeListener { + @Override + public void + stateChanged(ChangeEvent e) { + saveServerList(); + } + } + + private static final Vector idtmListeners = new Vector(); private static InstrumentsDbTreeModel instrumentsDbTreeModel = null; + /** * Gets the tree model of the instruments database. * If the currently used view doesn't have instruments @@ -380,15 +423,32 @@ */ public static InstrumentsDbTreeModel getInstrumentsDbTreeModel() { - if(!CC.getSamplerModel().getServerInfo().hasInstrumentsDbSupport()) return null; + if(getSamplerModel().getServerInfo() == null) return null; + if(!getSamplerModel().getServerInfo().hasInstrumentsDbSupport()) return null; if(instrumentsDbTreeModel == null) { instrumentsDbTreeModel = new InstrumentsDbTreeModel(); + for(ChangeListener l : idtmListeners) l.stateChanged(null); } return instrumentsDbTreeModel; } + public static void + addInstrumentsDbChangeListener(ChangeListener l) { + idtmListeners.add(l); + } + + public static void + removeInstrumentsDbChangeListener(ChangeListener l) { + idtmListeners.remove(l); + } + + private static final LostFilesModel lostFilesModel = new LostFilesModel(); + + public static LostFilesModel + getLostFilesModel() { return lostFilesModel; } + /** * Loads the orchestras described in <jsampler_home>/orchestras.xml. * If file with name orchestras.xml.bkp exist in the JSampler's home @@ -400,18 +460,8 @@ loadOrchestras() { if(getJSamplerHome() == null) return; - //TODO: This should be removed in the next release (including loadOrchestras0()) - File f2 = new File(getJSamplerHome() + File.separator + "orchestras.xml"); - if(!f2.isFile()) { - loadOrchestras0(); - saveOrchestras(); - return; - } - /////// - try { String s = getJSamplerHome(); - if(s == null) return; File f = new File(s + File.separator + "orchestras.xml.bkp"); if(f.isFile()) HF.createBackup("orchestras.xml.bkp", "orchestras.xml.rec"); @@ -424,6 +474,8 @@ } catch(Exception x) { getLogger().log(Level.INFO, HF.getErrorMessage(x), x); } + + getOrchestras().addOrchestraListListener(getHandler()); } @@ -440,19 +492,6 @@ for(int i = 0; i < getOrchestras().getOrchestraCount(); i++) { getOrchestras().getOrchestra(i).addOrchestraListener(getHandler()); } - getOrchestras().addOrchestraListListener(getHandler()); - } - - private static void - loadOrchestras0() { - String s = Prefs.getOrchestras(); - if(s == null) return; - - ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes()); - Document doc = DOMUtils.readObject(bais); - - try { getOrchestras().readObject(doc.getDocumentElement()); } - catch(Exception x) { HF.showErrorMessage(x, "Loading orchestras: "); } } private static void @@ -463,8 +502,8 @@ HF.createBackup("orchestras.xml", "orchestras.xml.bkp"); - FileOutputStream fos; - fos = new FileOutputStream(s + File.separator + "orchestras.xml", false); + FileOutputStream fos2; + fos2 = new FileOutputStream(s + File.separator + "orchestras.xml", false); Document doc = DOMUtils.createEmptyDocument(); @@ -475,9 +514,9 @@ doc.replaceChild(node.getFirstChild(), node); - DOMUtils.writeObject(doc, fos); + DOMUtils.writeObject(doc, fos2); - fos.close(); + fos2.close(); HF.deleteFile("orchestras.xml.bkp"); } catch(Exception x) { @@ -487,6 +526,87 @@ } /** + * Loads the servers' info described in <jsampler_home>/servers.xml. + * If file with name servers.xml.bkp exist in the JSampler's home + * directory, this means that the last save has failed. In that case a recovery file + * servers.xml.rec is created and a recovery procedure + * will be initiated. + */ + public static void + loadServerList() { + if(getJSamplerHome() == null) return; + + try { + String s = getJSamplerHome(); + + File f = new File(s + File.separator + "servers.xml.bkp"); + if(f.isFile()) HF.createBackup("servers.xml.bkp", "servers.xml.rec"); + + FileInputStream fis; + fis = new FileInputStream(s + File.separator + "servers.xml"); + + loadServerList(fis); + fis.close(); + } catch(Exception x) { + getLogger().log(Level.INFO, HF.getErrorMessage(x), x); + } + + getServerList().addChangeListener(serverListListener); + + /* We should have at least one server to connect. */ + if(getServerList().getServerCount() == 0) { + Server server = new Server(); + server.setName("127.0.0.1:8888"); + server.setAddress("127.0.0.1"); + server.setPort(8888); + getServerList().addServer(server); + } + } + + + private static void + loadServerList(InputStream in) { + Document doc = DOMUtils.readObject(in); + + try { getServerList().readObject(doc.getDocumentElement()); } + catch(Exception x) { + HF.showErrorMessage(x, "Loading server list: "); + return; + } + } + + private static void + saveServerList() { + try { + String s = getJSamplerHome(); + if(s == null) return; + + HF.createBackup("servers.xml", "servers.xml.bkp"); + + FileOutputStream fos2; + fos2 = new FileOutputStream(s + File.separator + "servers.xml", false); + + Document doc = DOMUtils.createEmptyDocument(); + + Node node = doc.createElement("temp"); + doc.appendChild(node); + + getServerList().writeObject(doc, doc.getDocumentElement()); + + doc.replaceChild(node.getFirstChild(), node); + + DOMUtils.writeObject(doc, fos2); + + fos2.close(); + + HF.deleteFile("servers.xml.bkp"); + } catch(Exception x) { + HF.showErrorMessage(x, "Saving server list: "); + return; + } + } + + /** * The exit point of the application which ensures clean exit with default exit status 0. * @see #cleanExit(int i) */ @@ -499,7 +619,12 @@ */ public static void cleanExit(int i) { - CC.getLogger().fine("CC.jsEnded"); + getLogger().fine("CC.jsEnded"); + try { getClient().disconnect(); } // FIXME: this might block the EDT + catch(Exception x) { x.printStackTrace(); } + if(backendProcess != null) backendProcess.destroy(); + backendProcess = null; + fireBackendProcessEvent(); System.exit(i); } @@ -532,6 +657,29 @@ for(ActionListener l : listeners) l.actionPerformed(e); } + private static final Vector ceListeners = new Vector(); + + /** + * Registers the specified listener to be notified when + * jsampler is connected successfully to LinuxSampler. + * @param l The ActionListener to register. + */ + public static void + addConnectionEstablishedListener(ActionListener l) { ceListeners.add(l); } + + /** + * Removes the specified listener. + * @param l The ActionListener to remove. + */ + public static void + removeConnectionEstablishedListener(ActionListener l) { ceListeners.remove(l); } + + private static void + fireConnectionEstablishedEvent() { + ActionEvent e = new ActionEvent(CC.class, ActionEvent.ACTION_PERFORMED, null); + for(ActionListener l : ceListeners) l.actionPerformed(e); + } + private static final SamplerModel samplerModel = new DefaultSamplerModel(); /** @@ -542,19 +690,63 @@ getSamplerModel() { return samplerModel; } /** + * Connects to LinuxSampler. + */ + public static void + connect() { initSamplerModel(); } + + /** * Reconnects to LinuxSampler. */ public static void - reconnect() { - initSamplerModel(); - fireReconnectEvent(); + reconnect() { initSamplerModel(getCurrentServer()); } + + private static Server currentServer = null; + + /** + * Gets the server, to which the frontend is going to connect + * or is already connected. + */ + public static Server + getCurrentServer() { return currentServer; } + + /** + * Sets the current server. + */ + public static void + setCurrentServer(Server server) { + if(server == currentServer) return; + connectionFailureCount = 0; + currentServer = server; } /** - * This method updates the information about the backend state. + * Sets the LSCP client's read timeout. + * @param timeout The new timeout value (in seconds). */ public static void + setClientReadTimeout(int timeout) { + getTaskQueue().add(new Global.SetClientReadTimeout(timeout)); + } + + /** + * This method updates the information about the backend state. + */ + private static void initSamplerModel() { + Server srv = getMainFrame().getServer(); + if(srv == null) return; + initSamplerModel(srv); + } + + /** + * This method updates the information about the backend state. + */ + private static void + initSamplerModel(Server srv) { + setCurrentServer(srv); + final SetServerAddress ssa = new SetServerAddress(srv.getAddress(), srv.getPort()); + final DefaultSamplerModel model = (DefaultSamplerModel)getSamplerModel(); final Global.GetServerInfo gsi = new Global.GetServerInfo(); @@ -580,7 +772,7 @@ } }); - final GetEngines ge = new GetEngines(); + final Global.GetEngines ge = new Global.GetEngines(); ge.addTaskListener(new TaskListener() { public void taskPerformed(TaskEvent e) { @@ -631,15 +823,23 @@ gfs.addTaskListener(new GetFxSendsListener()); getTaskQueue().add(gfs); } + + // TODO: This should be done after the fx sends are set + //CC.getSamplerModel().setModified(false); } }); - final Connect cnt = new Connect(); + final Global.Connect cnt = new Global.Connect(); + boolean b = preferences().getBoolProperty(JSPrefs.LAUNCH_BACKEND_LOCALLY); + if(b && srv.isLocal() && backendProcess == null) cnt.setSilent(true); cnt.addTaskListener(new TaskListener() { public void taskPerformed(TaskEvent e) { - if(cnt.doneWithErrors()) return; + if(cnt.doneWithErrors()) { + onConnectFailure(); + return; + } getTaskQueue().add(gsi); getTaskQueue().add(gaod); @@ -649,13 +849,150 @@ getTaskQueue().add(mgim); getTaskQueue().add(new Midi.UpdateDevices()); getTaskQueue().add(new Audio.UpdateDevices()); - getTaskQueue().add(uc); + addTask(uc); + + int vl = preferences().getIntProperty(JSPrefs.GLOBAL_VOICE_LIMIT); + int sl = preferences().getIntProperty(JSPrefs.GLOBAL_STREAM_LIMIT); + + getTaskQueue().add(new Global.SetPolyphony(vl, sl)); + + fireConnectionEstablishedEvent(); } }); - getTaskQueue().add(cnt); + + ssa.addTaskListener(new TaskListener() { + public void + taskPerformed(TaskEvent e) { + int t = preferences().getIntProperty(JSPrefs.SOCKET_READ_TIMEOUT); + CC.setClientReadTimeout(t * 1000); + CC.getTaskQueue().add(cnt); + } + }); + + getSamplerModel().reset(); + if(instrumentsDbTreeModel != null) { + instrumentsDbTreeModel.reset(); + instrumentsDbTreeModel = null; + } + + getTaskQueue().removePendingTasks(); + getTaskQueue().add(ssa); + + fireReconnectEvent(); + } + + private static void + onConnectFailure() { + connectionFailureCount++; + if(connectionFailureCount > 50) { // to prevent eventual infinite loop + getLogger().warning("Reached maximum number of connection failures"); + return; + } + + try { + if(launchBackend()) { + int i = preferences().getIntProperty(JSPrefs.BACKEND_LAUNCH_DELAY); + if(i < 1) { + initSamplerModel(getCurrentServer()); + return; + } + + LaunchBackend lb = new LaunchBackend(i, getBackendMonitor()); + //CC.getTaskQueue().add(lb); + new Thread(lb).start(); + return; + } + } catch(Exception x) { + final String s = JSI18n.i18n.getError("CC.failedToLaunchBackend"); + CC.getLogger().log(Level.INFO, s, x); + + SwingUtilities.invokeLater(new Runnable() { + public void + run() { HF.showErrorMessage(s); } + }); + return; + } + + retryToConnect(); + } + + private static void + retryToConnect() { + javax.swing.SwingUtilities.invokeLater(new Runnable() { + public void + run() { changeBackend(); } + }); + } + + public static void + changeBackend() { + Server s = getMainFrame().getServer(true); + if(s != null) { + connectionFailureCount = 0; // cleared because this change due to user interaction + initSamplerModel(s); + } + } + + private static final Vector pListeners = new Vector(); + + /** + * Registers the specified listener to be notified when + * backend process is created/terminated. + * @param l The ActionListener to register. + */ + public static void + addBackendProcessListener(ActionListener l) { pListeners.add(l); } + + /** + * Removes the specified listener. + * @param l The ActionListener to remove. + */ + public static void + removeBackendProcessListener(ActionListener l) { pListeners.remove(l); } + + private static void + fireBackendProcessEvent() { + ActionEvent e = new ActionEvent(CC.class, ActionEvent.ACTION_PERFORMED, null); + for(ActionListener l : pListeners) l.actionPerformed(e); + } + + private static Process backendProcess = null; + + public static Process + getBackendProcess() { return backendProcess; } + + private static final Object backendMonitor = new Object(); + + public static Object + getBackendMonitor() { return backendMonitor; } + + private static boolean + launchBackend() throws Exception { + if(backendProcess != null) { + try { + int i = backendProcess.exitValue(); + getLogger().info("Backend exited with exit value " + i); + backendProcess = null; + fireBackendProcessEvent(); + } catch(IllegalThreadStateException x) { return false; } + } + + if(!preferences().getBoolProperty(JSPrefs.LAUNCH_BACKEND_LOCALLY)) return false; + if(connectionFailureCount > 1) return false; + + Server s = getCurrentServer(); + if(s != null && s.isLocal()) { + String cmd = preferences().getStringProperty(JSPrefs.BACKEND_LAUNCH_COMMAND); + backendProcess = Runtime.getRuntime().exec(cmd); + fireBackendProcessEvent(); + return true; + } + + return false; } private static class GetFxSendsListener implements TaskListener { + @Override public void taskPerformed(TaskEvent e) { Channel.GetFxSends gfs = (Channel.GetFxSends)e.getSource(); @@ -689,13 +1026,13 @@ exportInstrMapsToLscpScript(Client lscpClient) { try { lscpClient.removeAllMidiInstrumentMaps(); - MidiInstrumentMap[] maps = CC.getSamplerModel().getMidiInstrumentMaps(); + MidiInstrumentMap[] maps = getSamplerModel().getMidiInstrumentMaps(); for(int i = 0; i < maps.length; i++) { lscpClient.addMidiInstrumentMap(maps[i].getName()); exportInstrumentsToLscpScript(i, maps[i], lscpClient); } } catch(Exception e) { - CC.getLogger().log(Level.FINE, HF.getErrorMessage(e), e); + getLogger().log(Level.FINE, HF.getErrorMessage(e), e); HF.showErrorMessage(e); } } @@ -704,13 +1041,17 @@ exportInstrumentsToLscpScript(int mapId, MidiInstrumentMap map, Client lscpClient) throws Exception { + boolean b = preferences().getBoolProperty(JSPrefs.LOAD_MIDI_INSTRUMENTS_IN_BACKGROUND); + for(MidiInstrument i : map.getAllMidiInstruments()) { - lscpClient.mapMidiInstrument(mapId, i.getInfo().getEntry(), i.getInfo()); + lscpClient.mapMidiInstrument(mapId, i.getInfo().getEntry(), i.getInfo(), b); } } public static String exportSessionToLscpScript() { + getSamplerModel().setModified(false); + StringBuffer sb = new StringBuffer("# Exported by: "); sb.append("JSampler - a java front-end for LinuxSampler\r\n# Version: "); sb.append(JSampler.VERSION).append("\r\n"); @@ -725,11 +1066,11 @@ sb.append(out.toString()); out.reset(); sb.append("\r\n"); - lscpClient.setVolume(CC.getSamplerModel().getVolume()); + lscpClient.setVolume(getSamplerModel().getVolume()); sb.append(out.toString()); out.reset(); sb.append("\r\n"); - } catch(Exception e) { CC.getLogger().log(Level.FINE, HF.getErrorMessage(e), e); } + } catch(Exception e) { getLogger().log(Level.FINE, HF.getErrorMessage(e), e); } MidiDeviceModel[] mDevs = getSamplerModel().getMidiDevices(); for(int i = 0; i < mDevs.length; i++) { @@ -747,10 +1088,18 @@ sb.append("\r\n"); } + boolean b = preferences().getBoolProperty(JSPrefs.EXPORT_MIDI_MAPS_TO_SESSION_SCRIPT); + if(b) { + exportInstrMapsToLscpScript(lscpClient); + sb.append(out.toString()); + out.reset(); + sb.append("\r\n"); + } + SamplerChannelModel[] channels = getSamplerModel().getChannels(); for(int i = 0; i < channels.length; i++) { - SamplerChannelModel scm = getSamplerModel().getChannelById(i); + SamplerChannelModel scm = channels[i]; exportChannelToLscpScript(scm.getChannelInfo(), i, lscpClient); sb.append(out.toString()); out.reset(); @@ -764,9 +1113,7 @@ sb.append("\r\n"); } - exportInstrMapsToLscpScript(lscpClient); - sb.append(out.toString()); - out.reset(); + sb.append(getViewConfig().exportSessionViewConfig()); return sb.toString(); } @@ -789,7 +1136,7 @@ } } } catch(Exception e) { - CC.getLogger().log(Level.FINE, HF.getErrorMessage(e), e); + getLogger().log(Level.FINE, HF.getErrorMessage(e), e); } } @@ -809,7 +1156,7 @@ } } } catch(Exception e) { - CC.getLogger().log(Level.FINE, HF.getErrorMessage(e), e); + getLogger().log(Level.FINE, HF.getErrorMessage(e), e); } } @@ -818,14 +1165,35 @@ try { lscpCLient.addSamplerChannel(); - int i = chn.getMidiInputDevice(); - if(i != -1) lscpCLient.setChannelMidiInputDevice(chnId, i); - lscpCLient.setChannelMidiInputPort(chnId, chn.getMidiInputPort()); - lscpCLient.setChannelMidiInputChannel(chnId, chn.getMidiInputChannel()); - - i = chn.getAudioOutputDevice(); - if(i != -1) { - lscpCLient.setChannelAudioOutputDevice(chnId, i); + SamplerModel sm = getSamplerModel(); + int id = chn.getMidiInputDevice(); + if(id != -1) { + for(int i = 0; i < sm.getMidiDeviceCount(); i++) { + if(sm.getMidiDevice(i).getDeviceId() == id) { + lscpCLient.setChannelMidiInputDevice(chnId, i); + break; + } + } + lscpCLient.setChannelMidiInputPort(chnId, chn.getMidiInputPort()); + lscpCLient.setChannelMidiInputChannel(chnId, chn.getMidiInputChannel()); + } + + if(chn.getEngine() != null) { + lscpCLient.loadSamplerEngine(chn.getEngine().getName(), chnId); + lscpCLient.setChannelVolume(chnId, chn.getVolume()); + int mapId = chn.getMidiInstrumentMapId(); + lscpCLient.setChannelMidiInstrumentMap(chnId, mapId); + } + + id = chn.getAudioOutputDevice(); + if(id != -1) { + for(int i = 0; i < sm.getAudioDeviceCount(); i++) { + if(sm.getAudioDevice(i).getDeviceId() == id) { + lscpCLient.setChannelAudioOutputDevice(chnId, i); + break; + } + } + Integer[] routing = chn.getAudioOutputRouting(); for(int j = 0; j < routing.length; j++) { @@ -836,19 +1204,14 @@ } } - if(chn.getEngine() != null) { - lscpCLient.loadSamplerEngine(chn.getEngine().getName(), chnId); - lscpCLient.setChannelVolume(chnId, chn.getVolume()); - } - String s = chn.getInstrumentFile(); - i = chn.getInstrumentIndex(); + int i = chn.getInstrumentIndex(); if(s != null) lscpCLient.loadInstrument(s, i, chnId, true); if(chn.isMuted()) lscpCLient.setChannelMute(chnId, true); if(chn.isSoloChannel()) lscpCLient.setChannelSolo(chnId, true); } catch(Exception e) { - CC.getLogger().log(Level.FINE, HF.getErrorMessage(e), e); + getLogger().log(Level.FINE, HF.getErrorMessage(e), e); } } @@ -860,6 +1223,7 @@ for(int i = 0; i < fxSends.length; i++) { FxSend f = fxSends[i]; lscpClient.createFxSend(chnId, f.getMidiController(), f.getName()); + lscpClient.setFxSendLevel(chnId, i, f.getLevel()); Integer[] r = f.getAudioOutputRouting(); for(int j = 0; j < r.length; j++) { @@ -867,8 +1231,36 @@ } } } catch(Exception e) { - CC.getLogger().log(Level.FINE, HF.getErrorMessage(e), e); + getLogger().log(Level.FINE, HF.getErrorMessage(e), e); + } + } + + public static void + scheduleInTaskQueue(final Runnable r) { + Task dummy = new Global.DummyTask(); + dummy.addTaskListener(new TaskListener() { + public void + taskPerformed(TaskEvent e) { + javax.swing.SwingUtilities.invokeLater(r); + } + }); + + getTaskQueue().add(dummy); + } + + public static boolean + verifyConnection() { + if(getCurrentServer() == null) { + HF.showErrorMessage(i18n.getError("CC.notConnected")); + return false; } + + return true; + } + + public static boolean + isMacOS() { + return System.getProperty("os.name").toLowerCase().startsWith("mac os x"); } @@ -879,17 +1271,27 @@ private static class EventHandler implements ChannelCountListener, ChannelInfoListener, FxSendCountListener, FxSendInfoListener, StreamCountListener, VoiceCountListener, - TotalVoiceCountListener, TaskQueueListener, OrchestraListener, - ListListener, MidiInstrumentCountListener, - MidiInstrumentInfoListener, GlobalInfoListener { + TotalStreamCountListener, TotalVoiceCountListener, TaskQueueListener, + OrchestraListener, ListListener, MidiInstrumentCountListener, + MidiInstrumentInfoListener, GlobalInfoListener, ChannelMidiDataListener { /** Invoked when the number of channels has changed. */ + @Override public void channelCountChanged( ChannelCountEvent e) { - getTaskQueue().add(new UpdateChannels()); + if(e.getChannelCount() == 0) { + /* + * This special case is handled because this might be due to + * loading a lscp script containing sampler view configuration. + */ + CC.getSamplerModel().removeAllChannels(); + return; + } + addTask(new UpdateChannels()); } /** Invoked when changes to the sampler channel has occured. */ + @Override public void channelInfoChanged(ChannelInfoEvent e) { /* @@ -927,6 +1329,7 @@ * Invoked when the number of effect sends * on a particular sampler channel has changed. */ + @Override public void fxSendCountChanged(FxSendCountEvent e) { getTaskQueue().add(new Channel.UpdateFxSends(e.getChannel())); @@ -935,6 +1338,7 @@ /** * Invoked when the settings of an effect sends are changed. */ + @Override public void fxSendInfoChanged(FxSendInfoEvent e) { Task t = new Channel.UpdateFxSendInfo(e.getChannel(), e.getFxSend()); @@ -945,6 +1349,7 @@ * Invoked when the number of active disk * streams in a specific sampler channel has changed. */ + @Override public void streamCountChanged(StreamCountEvent e) { SamplerChannelModel scm = @@ -967,6 +1372,7 @@ * Invoked when the number of active voices * in a specific sampler channel has changed. */ + @Override public void voiceCountChanged(VoiceCountEvent e) { SamplerChannelModel scm = @@ -985,19 +1391,29 @@ scm.setVoiceCount(e.getVoiceCount()); } + /** Invoked when the total number of active streams has changed. */ + @Override + public void + totalStreamCountChanged(TotalStreamCountEvent e) { + getSamplerModel().updateActiveStreamsInfo(e.getTotalStreamCount()); + } + /** Invoked when the total number of active voices has changed. */ + @Override public void totalVoiceCountChanged(TotalVoiceCountEvent e) { - getTaskQueue().add(new UpdateTotalVoiceCount()); + scheduleTask(new Global.UpdateTotalVoiceCount()); } /** Invoked when the number of MIDI instruments in a MIDI instrument map is changed. */ + @Override public void instrumentCountChanged(MidiInstrumentCountEvent e) { - getTaskQueue().add(new Midi.UpdateInstruments(e.getMapId())); + scheduleTask(new Midi.UpdateInstruments(e.getMapId())); } /** Invoked when a MIDI instrument in a MIDI instrument map is changed. */ + @Override public void instrumentInfoChanged(MidiInstrumentInfoEvent e) { Task t = new Midi.UpdateInstrumentInfo ( @@ -1008,15 +1424,25 @@ } /** Invoked when the global volume of the sampler is changed. */ + @Override public void volumeChanged(GlobalInfoEvent e) { getSamplerModel().setVolume(e.getVolume()); } + @Override + public void + voiceLimitChanged(GlobalInfoEvent e) { } + + @Override + public void + streamLimitChanged(GlobalInfoEvent e) { } + /** * Invoked to indicate that the state of a task queue is changed. * This method is invoked only from the event-dispatching thread. */ + @Override public void stateChanged(TaskQueueEvent e) { switch(e.getEventID()) { @@ -1027,8 +1453,12 @@ break; case TASK_DONE: EnhancedTask t = (EnhancedTask)e.getSource(); - if(t.doneWithErrors() && !t.isStopped()) { - showError(t); + if(t.doneWithErrors() && !t.isSilent()) { + if(t.getErrorCode() == t.SOCKET_ERROR) { + getMainFrame().handleConnectionFailure(); + } else if(!t.isStopped()) { + showError(t); + } } break; case NOT_IDLE: @@ -1060,26 +1490,32 @@ } /** Invoked when the name of orchestra is changed. */ + @Override public void nameChanged(OrchestraEvent e) { saveOrchestras(); } /** Invoked when the description of orchestra is changed. */ + @Override public void descriptionChanged(OrchestraEvent e) { saveOrchestras(); } /** Invoked when an instrument is added to the orchestra. */ + @Override public void instrumentAdded(OrchestraEvent e) { saveOrchestras(); } /** Invoked when an instrument is removed from the orchestra. */ + @Override public void instrumentRemoved(OrchestraEvent e) { saveOrchestras(); } /** Invoked when the settings of an instrument are changed. */ + @Override public void instrumentChanged(OrchestraEvent e) { saveOrchestras(); } /** Invoked when an orchestra is added to the orchestra list. */ + @Override public void entryAdded(ListEvent e) { e.getEntry().addOrchestraListener(getHandler()); @@ -1087,11 +1523,39 @@ } /** Invoked when an orchestra is removed from the orchestra list. */ + @Override public void entryRemoved(ListEvent e) { e.getEntry().removeOrchestraListener(getHandler()); saveOrchestras(); } + + /** + * Invoked when MIDI data arrives. + */ + @Override + public void + midiDataArrived(final ChannelMidiDataEvent e) { + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void + run() { fireChannelMidiDataEvent(e); } + }); + } catch(Exception x) { + CC.getLogger().log(Level.INFO, "Failed!", x); + } + } + } + + private static void + fireChannelMidiDataEvent(ChannelMidiDataEvent e) { + SamplerChannelModel chn; + chn = CC.getSamplerModel().getChannelById(e.getChannelId()); + if(chn == null) { + CC.getLogger().info("Unknown channel ID: " + e.getChannelId()); + } + + ((DefaultSamplerChannelModel)chn).fireMidiDataEvent(e); } private static final AudioDeviceCountListener audioDeviceCountListener = @@ -1099,6 +1563,7 @@ private static class AudioDeviceCountListener implements ItemCountListener { /** Invoked when the number of audio output devices has changed. */ + @Override public void itemCountChanged(ItemCountEvent e) { getTaskQueue().add(new Audio.UpdateDevices()); @@ -1110,6 +1575,7 @@ private static class AudioDeviceInfoListener implements ItemInfoListener { /** Invoked when the audio output device's settings are changed. */ + @Override public void itemInfoChanged(ItemInfoEvent e) { getTaskQueue().add(new Audio.UpdateDeviceInfo(e.getItemID())); @@ -1121,6 +1587,7 @@ private static class MidiDeviceCountListener implements ItemCountListener { /** Invoked when the number of MIDI input devices has changed. */ + @Override public void itemCountChanged(ItemCountEvent e) { getTaskQueue().add(new Midi.UpdateDevices()); @@ -1132,6 +1599,7 @@ private static class MidiDeviceInfoListener implements ItemInfoListener { /** Invoked when the MIDI input device's settings are changed. */ + @Override public void itemInfoChanged(ItemInfoEvent e) { getTaskQueue().add(new Midi.UpdateDeviceInfo(e.getItemID())); @@ -1143,6 +1611,7 @@ private static class MidiInstrMapCountListener implements ItemCountListener { /** Invoked when the number of MIDI instrument maps is changed. */ + @Override public void itemCountChanged(ItemCountEvent e) { getTaskQueue().add(new Midi.UpdateInstrumentMaps()); @@ -1154,6 +1623,7 @@ private static class MidiInstrMapInfoListener implements ItemInfoListener { /** Invoked when the MIDI instrument map's settings are changed. */ + @Override public void itemInfoChanged(ItemInfoEvent e) { getTaskQueue().add(new Midi.UpdateInstrumentMapInfo(e.getItemID()));