--- jlscp/trunk/src/org/linuxsampler/lscp/Client.java 2005/06/22 06:18:33 671 +++ jlscp/trunk/src/org/linuxsampler/lscp/Client.java 2011/08/17 09:06:46 2242 @@ -1,7 +1,7 @@ /* * jlscp - a java LinuxSampler control protocol API * - * Copyright (C) 2005 Grigor Kirilov Iliev + * Copyright (C) 2005-2011 Grigor Iliev * * This file is part of jlscp. * @@ -23,25 +23,28 @@ package org.linuxsampler.lscp; import java.io.IOException; +import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; -import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.TreeMap; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; -import static org.linuxsampler.lscp.Parser.*; import org.linuxsampler.lscp.event.*; +import static org.linuxsampler.lscp.Parser.*; + /** * This class is the abstraction representing a client endpoint for communication with LinuxSampler - * instance. Since it implements all commands specified in the LSCP protocol v1.0, for more + * instance. Since it implements all commands specified in the LSCP protocol v1.5, for more * information look at the - * LSCP specification. + * LSCP specification. * *

The following code establishes connection to LinuxSampler instance and gets the * LinuxSampler version: @@ -68,23 +71,47 @@ private String address; private int port; private Socket sock = null; - private int soTimeout = 10000; + private int soTimeout = 20000; private LscpInputStream in = null; private LscpOutputStream out = null; private EventThread eventThread; + private boolean printOnlyMode = false; + + public enum ResultSetType { + EMPTY, SINGLE_LINE, MULTI_LINE + } + + private static class ResultSetEntry { + public ResultSetType type; + + public + ResultSetEntry(ResultSetType type) { + this.type = type; + } + } + + private Vector resultSetQueue = new Vector(); + + /* Used for caching the engines' info */ + private final TreeMap engineMap = new TreeMap(); + class EventThread extends Thread { + private Vector queue = new Vector(); private boolean terminate = false; EventThread() { super("LSCP-Event-Thread"); } + @Override public void run() { while(!mustTerminate()) { - try { processNotifications(); } - catch(Exception x) { + try { + processQueue(); + processNotifications(); + } catch(Exception x) { getLogger().log(Level.FINE, x.getMessage(), x); } try { synchronized(this) { wait(100); } } @@ -102,6 +129,22 @@ terminate = true; this.notifyAll(); } + + public synchronized void + scheduleNotification(String s) { queue.add(s); } + + private void + processQueue() { + String[] notifications = popAllNotifications(); + for(String n : notifications) fireEvent(n); + } + + private synchronized String[] + popAllNotifications() { + String[] notifications = queue.toArray(new String[queue.size()]); + queue.removeAllElements(); + return notifications; + } } /** @@ -133,6 +176,108 @@ } /** + * Creates a new instance of Client. + * @param printOnlyMode Determines whether the client will be in print-only mode. + */ + public + Client(boolean printOnlyMode) { + if(printOnlyMode) setPrintOnlyMode(true); + } + + private boolean extendedCharacterEscaping = true; + + /** + * Sets whether strings sent to LinuxSampler should be more aggressively escaped. + */ + public synchronized void + setExtendedCharacterEscaping(boolean b) { extendedCharacterEscaping = b; } + + /** + * Determines whether strings sent to LinuxSampler should be more aggressively escaped. + */ + public synchronized boolean + getExtendedCharacterEscaping() { return extendedCharacterEscaping; } + + /** + * @see java.net.Socket#setSoTimeout + */ + public synchronized void + setSoTimeout(int timeout) { + soTimeout = timeout; + + try { if(sock != null) sock.setSoTimeout(timeout); } + catch(Exception x) { getLogger().log(Level.INFO, "Unable to set timeout", x); } + } + + private String + toEscapedText(String s) { + s = toEscapedString(s); + return conv(s); + } + + private String + toEscapedFsEntry(String s) { + s = toEscapedFileName(s); + return conv(s); + } + + /** + * Applies an extended character escaping to the specified string if needed. + */ + private String + conv(String s) { + return getExtendedCharacterEscaping() ? toExtendedEscapeSequence(s) : s; + } + + /** + * Determines whether the client is in print-only mode. + * Print-only mode means that the client will just print all + * LSCP commands to the specified output stream or to the standard output stream + * (java.lang.System.out) if no output stream is specified, + * without taking any further actions. Thus, in print-only mode all returned + * values by Client's methods are meaningless and should be discarded. + * @return true if the client is in + * print-only mode, false otherwise. + * @see #setPrintOnlyModeOutputStream + */ + public synchronized boolean + getPrintOnlyMode() { return printOnlyMode; } + + /** + * Sets the print-only mode. Note that in print-only mode all returned + * values by Client's methods are meaningless and should be discarded. + * The default output stream in print-only mode is java.lang.System.out. + * @param b If true all LSCP commands will be sent + * to the specified output stream or to the standard output stream + * (java.lang.System.out) if no output stream is specified, + * and no further actions will be taken. + * @throws IllegalStateException If the client is connected. + * @see #setPrintOnlyModeOutputStream + */ + public synchronized void + setPrintOnlyMode(boolean b) { + if(printOnlyMode == b) return; + if(isConnected()) throw new IllegalStateException(); + + printOnlyMode = b; + if(b) out = new LscpOutputStream(System.out); + } + + /** + * Sets the output stream to be used in print-only mode. + * @param out The output stream to be used in print-only mode. + * @throws IllegalStateException If the client is not in print-only mode. + * @throws IllegalArgumentException if out is null. + * @see #setPrintOnlyMode + */ + public synchronized void + setPrintOnlyModeOutputStream(OutputStream out) { + if(!getPrintOnlyMode()) throw new IllegalStateException("Not in print-only mode"); + if(out == null) throw new IllegalArgumentException("out must be non-null"); + this.out = new LscpOutputStream(out); + } + + /** * Specifies the jlscp version. * @return The jlscp version. */ @@ -180,6 +325,9 @@ public synchronized void connect() throws LscpException { if(sock != null) disconnect(); + if(getPrintOnlyMode()) return; + + engineMap.clear(); // Initializing LSCP event thread if(eventThread.isAlive()) { @@ -207,6 +355,7 @@ sock.bind(null); sock.connect(sockAddr, soTimeout); sock.setSoTimeout(soTimeout); + sock.setTcpNoDelay(true); in = new LscpInputStream(sock.getInputStream()); out = new LscpOutputStream(sock.getOutputStream()); @@ -251,11 +400,37 @@ if(hasSubscriptions()) eventThread.start(); if(!llM.isEmpty()) subscribe("MISCELLANEOUS"); + if(!llAODC.isEmpty()) subscribe("AUDIO_OUTPUT_DEVICE_COUNT"); + if(!llAODI.isEmpty()) subscribe("AUDIO_OUTPUT_DEVICE_INFO"); + if(!llMIDC.isEmpty()) subscribe("MIDI_INPUT_DEVICE_COUNT"); + if(!llMIDI.isEmpty()) subscribe("MIDI_INPUT_DEVICE_INFO"); if(!llBF.isEmpty()) subscribe("BUFFER_FILL"); if(!llCC.isEmpty()) subscribe("CHANNEL_COUNT"); if(!llCI.isEmpty()) subscribe("CHANNEL_INFO"); + if(!llFSC.isEmpty()) subscribe("FX_SEND_COUNT"); + if(!llFSI.isEmpty()) subscribe("FX_SEND_INFO"); if(!llSC.isEmpty()) subscribe("STREAM_COUNT"); if(!llVC.isEmpty()) subscribe("VOICE_COUNT"); + if(!llTSC.isEmpty()) subscribe("TOTAL_STREAM_COUNT"); + if(!llTVC.isEmpty()) subscribe("TOTAL_VOICE_COUNT"); + if(!llMIMC.isEmpty()) subscribe("MIDI_INSTRUMENT_MAP_COUNT"); + if(!llMIMI.isEmpty()) subscribe("MIDI_INSTRUMENT_MAP_INFO"); + if(!llMIC.isEmpty()) subscribe("MIDI_INSTRUMENT_COUNT"); + if(!llMII.isEmpty()) subscribe("MIDI_INSTRUMENT_INFO"); + if(!llDMD.isEmpty()) subscribe("DEVICE_MIDI"); + if(!llCMD.isEmpty()) subscribe("CHANNEL_MIDI"); + if(!llID.isEmpty()) { + subscribe("DB_INSTRUMENT_DIRECTORY_COUNT"); + subscribe("DB_INSTRUMENT_DIRECTORY_INFO"); + subscribe("DB_INSTRUMENT_COUNT"); + subscribe("DB_INSTRUMENT_INFO"); + subscribe("DB_INSTRUMENTS_JOB_INFO"); + } + if(!llGI.isEmpty()) subscribe("GLOBAL_INFO"); + if(!llEIC.isEmpty()) subscribe("EFFECT_INSTANCE_COUNT"); + if(!llEII.isEmpty()) subscribe("EFFECT_INSTANCE_INFO"); + if(!llSECC.isEmpty()) subscribe("SEND_EFFECT_CHAIN_COUNT"); + if(!llSECI.isEmpty()) subscribe("SEND_EFFECT_CHAIN_INFO"); } /** @@ -263,6 +438,7 @@ */ public synchronized void disconnect() { + if(getPrintOnlyMode()) return; try { if(sock != null) sock.close(); } catch(Exception x) { getLogger().log(Level.FINE, x.getMessage(), x); } sock = null; @@ -271,6 +447,8 @@ eventThread.terminate(); eventThread = new EventThread(); } + + engineMap.clear(); } /** @@ -290,6 +468,8 @@ */ private void verifyConnection() throws IOException { + if(getPrintOnlyMode()) return; + if(!isConnected()) throw new IOException(LscpI18n.getLogMsg("Client.notConnected!")); } @@ -299,13 +479,15 @@ String s; for(;;) { s = in.readLine(); - if(s.startsWith("NOTIFY:")) fireEvent(s.substring("NOTIFY:".length())); + if(s.startsWith("NOTIFY:")) { + eventThread.scheduleNotification(s.substring("NOTIFY:".length())); + } else break; } return s; } - /** Processes the notifications send by LinuxSampler */ + /** Processes the notifications sent by LinuxSampler */ private synchronized void processNotifications() throws IOException, LscpException { while(in.available() > 0) { @@ -314,18 +496,45 @@ else getLogger().severe("Unknown notification format: " + s); } } + + private synchronized void + processResultSetQueue() { + for(int i = 0; i < resultSetQueue.size(); i++) { + try { + switch(resultSetQueue.get(i).type) { + case EMPTY: + getEmptyResultSet(); + break; + case SINGLE_LINE: + getSingleLineResultSet(); + break; + case MULTI_LINE: + getMultiLineResultSet(); + break; + default: + getLogger().severe("Unknown result set type"); + } + } catch(Exception x) { + getLogger().log(Level.FINE, "Error while processing result set queue", x); + } + } + + resultSetQueue.removeAllElements(); + } /** * Gets empty result set. * @return ResultSet instance. */ private ResultSet - getEmptyResultSet() throws IOException, LscpException, LSException { + getEmptyResultSet() throws IOException, LscpException, LSException { + processResultSetQueue(); return parseEmptyResultSet(getLine()); } private ResultSet - getSingleLineResultSet() throws IOException, LscpException, LSException { + getSingleLineResultSet() throws IOException, LscpException, LSException { + processResultSetQueue(); ResultSet rs = new ResultSet(); String ln = getLine(); @@ -344,6 +553,7 @@ private ResultSet getMultiLineResultSet() throws IOException, LscpException, LSException { + processResultSetQueue(); ResultSet rs = new ResultSet(); String ln = getLine(); @@ -364,33 +574,228 @@ return rs; } + /** Audio output device count listeners */ + private final Vector llAODC = new Vector(); + /** Audio output device info listeners */ + private final Vector llAODI = new Vector(); private final Vector llBF = new Vector(); private final Vector llCC = new Vector(); private final Vector llCI = new Vector(); + private final Vector llFSC = new Vector(); + private final Vector llFSI = new Vector(); private final Vector llM = new Vector(); + /** MIDI input device count listeners */ + private final Vector llMIDC = new Vector(); + /** MIDI input device info listeners */ + private final Vector llMIDI = new Vector(); private final Vector llSC = new Vector(); private final Vector llVC = new Vector(); + private final Vector llTSC = new Vector(); + private final Vector llTVC = new Vector(); + + /** MIDI instrument map count listeners */ + private final Vector llMIMC = new Vector(); + /** MIDI instrument map info listeners */ + private final Vector llMIMI = new Vector(); + /** MIDI instrument count listeners */ + private final Vector llMIC = + new Vector(); + /** MIDI instrument info listeners */ + private final Vector llMII = + new Vector(); + private final Vector llDMD = new Vector(); + private final Vector llCMD = new Vector(); + private final Vector llID = new Vector(); + private final Vector llGI = new Vector(); + private final ArrayList llEIC = new ArrayList(); + private final Vector llEII = new Vector(); + private final Vector llSECC = new Vector(); + private final Vector llSECI = new Vector(); + /** * Determines whether there is at least one subscription for notification events. * Do not forget to check for additional listeners if the LSCP specification - * extends in the future. + * is extended in the future. * @return true if there is at least one subscription for notification events, * false otherwise. */ private boolean hasSubscriptions() { - return !llBF.isEmpty() || - !llCC.isEmpty() || - !llCI.isEmpty() || - !llM.isEmpty() || - !llSC.isEmpty() || - !llVC.isEmpty(); + return !llAODC.isEmpty() || + !llAODI.isEmpty() || + !llBF.isEmpty() || + !llCC.isEmpty() || + !llCI.isEmpty() || + !llFSC.isEmpty() || + !llFSI.isEmpty() || + !llM.isEmpty() || + !llMIDC.isEmpty() || + !llMIDI.isEmpty() || + !llSC.isEmpty() || + !llVC.isEmpty() || + !llTSC.isEmpty() || + !llTVC.isEmpty() || + !llMIMC.isEmpty() || + !llMIMI.isEmpty() || + !llMIC.isEmpty() || + !llMII.isEmpty() || + !llDMD.isEmpty() || + !llCMD.isEmpty() || + !llID.isEmpty() || + !llGI.isEmpty() || + !llEIC.isEmpty() || + !llEII.isEmpty() || + !llSECC.isEmpty() || + !llSECI.isEmpty(); } - private void + private synchronized void + fireDeviceMidiDataEvent(String s) { + try { + String[] list = parseList(s, ' '); + if(list.length != 5) { + getLogger().warning("Unknown DEVICE_MIDI format"); + return; + } + + int dev = parseInt(list[0]); + int port = parseInt(list[1]); + + MidiDataEvent.Type type = parseMidiDataType(list[2]); + if(type == null) return; + + int note = parseInt(list[3]); + int velocity = parseInt(list[4]); + + DeviceMidiDataEvent e = new DeviceMidiDataEvent(this, type, note, velocity); + e.setDeviceId(dev); + e.setPortId(port); + for(DeviceMidiDataListener l : llDMD) l.midiDataArrived(e); + } catch(LscpException x) { + getLogger().log ( + Level.WARNING, LscpI18n.getLogMsg("CommandFailed!"), x + ); + } + } + + private synchronized void + fireChannelMidiDataEvent(String s) { + try { + String[] list = parseList(s, ' '); + if(list.length != 4) { + getLogger().warning("Unknown CHANNEL_MIDI format"); + return; + } + + int channel = parseInt(list[0]); + + MidiDataEvent.Type type = parseMidiDataType(list[1]); + if(type == null) return; + + int note = parseInt(list[2]); + int velocity = parseInt(list[3]); + + ChannelMidiDataEvent e = new ChannelMidiDataEvent(this, type, note, velocity); + e.setChannelId(channel); + for(ChannelMidiDataListener l : llCMD) l.midiDataArrived(e); + } catch(LscpException x) { + getLogger().log ( + Level.WARNING, LscpI18n.getLogMsg("CommandFailed!"), x + ); + } + } + + private MidiDataEvent.Type + parseMidiDataType(String s) { + if("NOTE_ON".equals(s)) return MidiDataEvent.Type.NOTE_ON; + if("NOTE_OFF".equals(s)) return MidiDataEvent.Type.NOTE_OFF; + if("CC".equals(s)) return MidiDataEvent.Type.CC; + + getLogger().warning("Unknown MIDI data type: " + s); + return null; + } + + private synchronized void fireEvent(String s) { - if(s.startsWith("CHANNEL_COUNT:")) { + // Sort by priority + + if(s.startsWith("CHANNEL_MIDI:")) { + s = s.substring("CHANNEL_MIDI:".length()); + fireChannelMidiDataEvent(s); + } else if(s.startsWith("DEVICE_MIDI:")) { + s = s.substring("DEVICE_MIDI:".length()); + fireDeviceMidiDataEvent(s); + } else if(s.startsWith("DB_INSTRUMENT_DIRECTORY_COUNT:")) { + s = s.substring("DB_INSTRUMENT_DIRECTORY_COUNT:".length()); + InstrumentsDbEvent e = new InstrumentsDbEvent(this, s); + for(InstrumentsDbListener l : llID) l.directoryCountChanged(e); + } else if(s.startsWith("DB_INSTRUMENT_DIRECTORY_INFO:")) { + InstrumentsDbEvent e; + s = s.substring("DB_INSTRUMENT_DIRECTORY_INFO:".length()); + if(s.startsWith("NAME ")) { + String[] list; + try { + s = s.substring("NAME ".length()); + list = parseEscapedStringList(s, ' '); + if(list.length != 2) throw new LscpException(); + list[1] = toNonEscapedString(list[1]); + e = new InstrumentsDbEvent(this, list[0], list[1]); + for(InstrumentsDbListener l : llID) { + l.directoryNameChanged(e); + } + } catch(LscpException x) { + getLogger().log ( + Level.WARNING, + LscpI18n.getLogMsg("CommandFailed!"), + x + ); + } + } else { + e = new InstrumentsDbEvent(this, s); + for(InstrumentsDbListener l : llID) l.directoryInfoChanged(e); + } + } else if(s.startsWith("DB_INSTRUMENT_COUNT:")) { + s = s.substring("DB_INSTRUMENT_COUNT:".length()); + InstrumentsDbEvent e = new InstrumentsDbEvent(this, s); + for(InstrumentsDbListener l : llID) l.instrumentCountChanged(e); + } else if(s.startsWith("DB_INSTRUMENT_INFO:")) { + InstrumentsDbEvent e; + s = s.substring("DB_INSTRUMENT_INFO:".length()); + if(s.startsWith("NAME ")) { + String[] list; + try { + s = s.substring("NAME ".length()); + list = parseEscapedStringList(s, ' '); + if(list.length != 2) throw new LscpException(); + list[1] = toNonEscapedString(list[1]); + e = new InstrumentsDbEvent(this, list[0], list[1]); + for(InstrumentsDbListener l : llID) { + l.instrumentNameChanged(e); + } + } catch(LscpException x) { + getLogger().log ( + Level.WARNING, + LscpI18n.getLogMsg("CommandFailed!"), + x + ); + } + } else { + e = new InstrumentsDbEvent(this, s); + for(InstrumentsDbListener l : llID) l.instrumentInfoChanged(e); + } + } else if(s.startsWith("DB_INSTRUMENTS_JOB_INFO:")) { + s = s.substring("DB_INSTRUMENTS_JOB_INFO:".length()); + try { + int i = Integer.parseInt(s); + InstrumentsDbEvent e = new InstrumentsDbEvent(this, i); + for(InstrumentsDbListener l : llID) l.jobStatusChanged(e); + } catch(NumberFormatException x) { + s = "Unknown DB_INSTRUMENTS_JOB_INFO format"; + getLogger().log(Level.WARNING, s, x); + } + + } else if(s.startsWith("CHANNEL_COUNT:")) { try { int i = Integer.parseInt(s.substring("CHANNEL_COUNT:".length())); ChannelCountEvent e = new ChannelCountEvent(this, i); @@ -403,31 +808,27 @@ } else if(s.startsWith("VOICE_COUNT:")) { try { s = s.substring("VOICE_COUNT:".length()); - int i = s.indexOf(' '); - if(i == -1) { + Integer[] i = parseIntList(s, ' '); + if(i.length != 2) { getLogger().warning("Unknown VOICE_COUNT format"); return; } - int j = Integer.parseInt(s.substring(0, i)); - i = Integer.parseInt(s.substring(i + 1)); - VoiceCountEvent e = new VoiceCountEvent(this, j, i); + VoiceCountEvent e = new VoiceCountEvent(this, i[0], i[1]); for(VoiceCountListener l : llVC) l.voiceCountChanged(e); - } catch(NumberFormatException x) { + } catch(Exception x) { getLogger().log(Level.WARNING, "Unknown VOICE_COUNT format", x); } } else if(s.startsWith("STREAM_COUNT:")) { try { s = s.substring("STREAM_COUNT:".length()); - int i = s.indexOf(' '); - if(i == -1) { + Integer[] i = parseIntList(s, ' '); + if(i.length != 2) { getLogger().warning("Unknown STREAM_COUNT format"); return; } - int j = Integer.parseInt(s.substring(0, i)); - i = Integer.parseInt(s.substring(i + 1)); - StreamCountEvent e = new StreamCountEvent(this, j, i); + StreamCountEvent e = new StreamCountEvent(this, i[0], i[1]); for(StreamCountListener l : llSC) l.streamCountChanged(e); - } catch(NumberFormatException x) { + } catch(Exception x) { getLogger().log(Level.WARNING, "Unknown STREAM_COUNT format", x); } } else if(s.startsWith("BUFFER_FILL:")) { @@ -435,7 +836,7 @@ s = s.substring("BUFFER_FILL:".length()); int i = s.indexOf(' '); if(i == -1) { - getLogger().warning("Unknown STREAM_COUNT format"); + getLogger().warning("Unknown BUFFER_FILL format"); return; } int j = Integer.parseInt(s.substring(0, i)); @@ -444,7 +845,7 @@ BufferFillEvent e = new BufferFillEvent(this, j, v); for(BufferFillListener l : llBF) l.bufferFillChanged(e); } catch(Exception x) { - getLogger().log(Level.WARNING, "Unknown STREAM_COUNT format", x); + getLogger().log(Level.WARNING, "Unknown BUFFER_FILL format", x); } } else if(s.startsWith("CHANNEL_INFO:")) { try { @@ -452,8 +853,228 @@ ChannelInfoEvent e = new ChannelInfoEvent(this, i); for(ChannelInfoListener l : llCI) l.channelInfoChanged(e); } catch(NumberFormatException x) { - getLogger().log(Level.WARNING, "Unknown STREAM_COUNT format", x); + getLogger().log(Level.WARNING, "Unknown CHANNEL_INFO format", x); + } + } else if(s.startsWith("TOTAL_STREAM_COUNT:")) { + try { + s = s.substring("TOTAL_STREAM_COUNT:".length()); + int i = Integer.parseInt(s); + TotalStreamCountEvent e = new TotalStreamCountEvent(this, i); + for(TotalStreamCountListener l : llTSC) l.totalStreamCountChanged(e); + } catch(NumberFormatException x) { + getLogger().log ( + Level.WARNING, "Unknown TOTAL_STREAM_COUNT format", x + ); + } + } else if(s.startsWith("TOTAL_VOICE_COUNT:")) { + try { + s = s.substring("TOTAL_VOICE_COUNT:".length()); + int i = Integer.parseInt(s); + TotalVoiceCountEvent e = new TotalVoiceCountEvent(this, i); + for(TotalVoiceCountListener l : llTVC) l.totalVoiceCountChanged(e); + } catch(NumberFormatException x) { + getLogger().log ( + Level.WARNING, "Unknown TOTAL_VOICE_COUNT format", x + ); + } + } else if(s.startsWith("AUDIO_OUTPUT_DEVICE_COUNT:")) { + try { + s = s.substring("AUDIO_OUTPUT_DEVICE_COUNT:".length()); + int i = Integer.parseInt(s); + ItemCountEvent e = new ItemCountEvent(this, i); + for(ItemCountListener l : llAODC) l.itemCountChanged(e); + } catch(NumberFormatException x) { + getLogger().log ( + Level.WARNING, "Unknown AUDIO_OUTPUT_DEVICE_COUNT format", x + ); + } + } else if(s.startsWith("AUDIO_OUTPUT_DEVICE_INFO:")) { + try { + s = s.substring("AUDIO_OUTPUT_DEVICE_INFO:".length()); + int i = Integer.parseInt(s); + ItemInfoEvent e = new ItemInfoEvent(this, i); + for(ItemInfoListener l : llAODI) l.itemInfoChanged(e); + } catch(NumberFormatException x) { + getLogger().log ( + Level.WARNING, "Unknown AUDIO_OUTPUT_DEVICE_INFO format", x + ); + } + } else if(s.startsWith("MIDI_INPUT_DEVICE_COUNT:")) { + try { + s = s.substring("MIDI_INPUT_DEVICE_COUNT:".length()); + int i = Integer.parseInt(s); + ItemCountEvent e = new ItemCountEvent(this, i); + for(ItemCountListener l : llMIDC) l.itemCountChanged(e); + } catch(NumberFormatException x) { + getLogger().log ( + Level.WARNING, "Unknown MIDI_INPUT_DEVICE_COUNT format", x + ); + } + } else if(s.startsWith("MIDI_INPUT_DEVICE_INFO:")) { + try { + s = s.substring("MIDI_INPUT_DEVICE_INFO:".length()); + int i = Integer.parseInt(s); + ItemInfoEvent e = new ItemInfoEvent(this, i); + for(ItemInfoListener l : llMIDI) l.itemInfoChanged(e); + } catch(NumberFormatException x) { + getLogger().log ( + Level.WARNING, "Unknown MIDI_INPUT_DEVICE_INFO format", x + ); + } + } else if(s.startsWith("MIDI_INSTRUMENT_MAP_COUNT:")) { + try { + s = s.substring("MIDI_INSTRUMENT_MAP_COUNT:".length()); + int i = Integer.parseInt(s); + ItemCountEvent e = new ItemCountEvent(this, i); + for(ItemCountListener l : llMIMC) l.itemCountChanged(e); + } catch(NumberFormatException x) { + getLogger().log ( + Level.WARNING, "Unknown MIDI_INSTRUMENT_MAP_COUNT format", x + ); + } + } else if(s.startsWith("MIDI_INSTRUMENT_MAP_INFO:")) { + try { + s = s.substring("MIDI_INSTRUMENT_MAP_INFO:".length()); + int i = Integer.parseInt(s); + ItemInfoEvent e = new ItemInfoEvent(this, i); + for(ItemInfoListener l : llMIMI) l.itemInfoChanged(e); + } catch(NumberFormatException x) { + getLogger().log ( + Level.WARNING, "Unknown MIDI_INSTRUMENT_MAP_INFO format", x + ); + } + } else if(s.startsWith("MIDI_INSTRUMENT_COUNT:")) { + try { + s = s.substring("MIDI_INSTRUMENT_COUNT:".length()); + Integer[] i = parseIntList(s, ' '); + if(i.length != 2) { + getLogger().warning("Unknown MIDI_INSTRUMENT_COUNT format"); + return; + } + + MidiInstrumentCountEvent e = + new MidiInstrumentCountEvent(this, i[0], i[1]); + + for(MidiInstrumentCountListener l : llMIC) { + l.instrumentCountChanged(e); + } + } catch(Exception x) { + getLogger().log ( + Level.WARNING, "Unknown MIDI_INSTRUMENT_COUNT format", x + ); + } + } else if(s.startsWith("MIDI_INSTRUMENT_INFO:")) { + try { + s = s.substring("MIDI_INSTRUMENT_INFO:".length()); + Integer[] i = parseIntList(s, ' '); + if(i.length != 3) { + getLogger().warning("Unknown MIDI_INSTRUMENT_INFO format"); + return; + } + + MidiInstrumentInfoEvent e = + new MidiInstrumentInfoEvent(this, i[0], i[1], i[2]); + for(MidiInstrumentInfoListener l : llMII) { + l.instrumentInfoChanged(e); + } + } catch(Exception x) { + getLogger().log ( + Level.WARNING, "Unknown MIDI_INSTRUMENT_INFO format", x + ); } + } else if(s.startsWith("FX_SEND_COUNT:")) { + try { + s = s.substring("FX_SEND_COUNT:".length()); + Integer[] i = parseIntList(s, ' '); + if(i.length != 2) { + getLogger().warning("Unknown FX_SEND_COUNT format"); + return; + } + + FxSendCountEvent e = new FxSendCountEvent(this, i[0], i[1]); + + for(FxSendCountListener l : llFSC) l.fxSendCountChanged(e); + } catch(Exception x) { + getLogger().log(Level.WARNING, "Unknown FX_SEND_COUNT format", x); + } + } else if(s.startsWith("FX_SEND_INFO:")) { + try { + s = s.substring("FX_SEND_INFO:".length()); + Integer[] i = parseIntList(s, ' '); + if(i.length != 2) { + getLogger().warning("Unknown FX_SEND_INFO format"); + return; + } + + FxSendInfoEvent e = new FxSendInfoEvent(this, i[0], i[1]); + for(FxSendInfoListener l : llFSI) { + l.fxSendInfoChanged(e); + } + } catch(Exception x) { + getLogger().log(Level.WARNING, "Unknown FX_SEND_INFO format", x); + } + } else if(s.startsWith("EFFECT_INSTANCE_COUNT:")) { + try { + s = s.substring("EFFECT_INSTANCE_COUNT:".length()); + int i = Integer.parseInt(s); + + EffectInstanceCountEvent e = new EffectInstanceCountEvent(this, i); + for(EffectInstanceCountListener l : llEIC) { + l.effectInstanceCountChanged(e); + } + } catch(Exception x) { + getLogger().log(Level.WARNING, "Unknown EFFECT_INSTANCE_COUNT format", x); + } + } else if(s.startsWith("EFFECT_INSTANCE_INFO:")) { + try { + s = s.substring("EFFECT_INSTANCE_INFO:".length()); + int i = Integer.parseInt(s); + + EffectInstanceInfoEvent e = new EffectInstanceInfoEvent(this, i); + for(EffectInstanceInfoListener l : llEII) { + l.effectInstanceInfoChanged(e); + } + } catch(Exception x) { + getLogger().log(Level.WARNING, "Unknown EFFECT_INSTANCE_INFO format", x); + } + } else if(s.startsWith("SEND_EFFECT_CHAIN_COUNT:")) { + try { + s = s.substring("SEND_EFFECT_CHAIN_COUNT:".length()); + Integer[] i = parseIntList(s, ' '); + if(i.length != 2) { + getLogger().warning("Unknown SEND_EFFECT_CHAIN_COUNT format"); + return; + } + + SendEffectChainCountEvent e = + new SendEffectChainCountEvent(this, i[0], i[1]); + + for(SendEffectChainCountListener l : llSECC) { + l.sendEffectChainCountChanged(e); + } + } catch(Exception x) { + getLogger().log(Level.WARNING, "Unknown SEND_EFFECT_CHAIN_COUNT format", x); + } + } else if(s.startsWith("SEND_EFFECT_CHAIN_INFO:")) { + try { + s = s.substring("SEND_EFFECT_CHAIN_INFO:".length()); + Integer[] i = parseIntList(s, ' '); + if(i.length != 3) { + getLogger().warning("Unknown SEND_EFFECT_CHAIN_INFO format"); + return; + } + + SendEffectChainInfoEvent e = + new SendEffectChainInfoEvent(this, i[0], i[1], i[2]); + + for(SendEffectChainInfoListener l : llSECI) { + l.sendEffectChainInfoChanged(e); + } + } catch(Exception x) { + getLogger().log(Level.WARNING, "Unknown SEND_EFFECT_CHAIN_INFO format", x); + } + } else if(s.startsWith("GLOBAL_INFO:")) { + handleGlobalInfoEvent(s.substring("GLOBAL_INFO:".length())); } else if(s.startsWith("MISCELLANEOUS:")) { s = s.substring("MISCELLANEOUS:".length()); MiscellaneousEvent e = new MiscellaneousEvent(this, s); @@ -462,14 +1083,39 @@ } private void + handleGlobalInfoEvent(String s) { + try { + if(s.startsWith("VOLUME ")) { + float f = Float.parseFloat(s.substring("VOLUME ".length())); + GlobalInfoEvent e = new GlobalInfoEvent(this, f); + for(GlobalInfoListener l : llGI) l.volumeChanged(e); + } else if(s.startsWith("VOICES ")) { + int i = Integer.parseInt(s.substring("VOICES ".length())); + GlobalInfoEvent e = new GlobalInfoEvent(this, i, -1); + for(GlobalInfoListener l : llGI) l.voiceLimitChanged(e); + } else if(s.startsWith("STREAMS ")) { + int i = Integer.parseInt(s.substring("STREAMS ".length())); + GlobalInfoEvent e = new GlobalInfoEvent(this, -1, i); + for(GlobalInfoListener l : llGI) l.streamLimitChanged(e); + } else { + getLogger().info("Unknown GLOBAL_INFO format: " + s); + } + } catch(NumberFormatException x) { + getLogger().log(Level.WARNING, "Unknown GLOBAL_INFO format", x); + } + } + + private void subscribe(String event) { - if(!isConnected()) return; + if(!getPrintOnlyMode()) { + if(!isConnected()) return; - if(!eventThread.isAlive()) eventThread.start(); + if(!eventThread.isAlive()) eventThread.start(); + } try { out.writeLine("SUBSCRIBE " + event); - ResultSet rs = getEmptyResultSet(); + if(!getPrintOnlyMode()) getEmptyResultSet(); } catch(Exception x) { getLogger().log ( Level.WARNING, @@ -481,11 +1127,11 @@ private void unsubscribe(String event) { - if(!isConnected()) return; + if(!getPrintOnlyMode() && !isConnected()) return; try { out.writeLine("UNSUBSCRIBE " + event); - ResultSet rs = getEmptyResultSet(); + if(!getPrintOnlyMode()) getEmptyResultSet(); } catch(Exception x) { getLogger().log ( Level.WARNING, @@ -497,6 +1143,50 @@ /** * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The ItemCountListener to register. + */ + public synchronized void + addAudioDeviceCountListener(ItemCountListener l) { + if(llAODC.isEmpty()) subscribe("AUDIO_OUTPUT_DEVICE_COUNT"); + llAODC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The ItemCountListener to remove. + */ + public synchronized void + removeAudioDeviceCountListener(ItemCountListener l) { + boolean b = llAODC.remove(l); + if(b && llAODC.isEmpty()) unsubscribe("AUDIO_OUTPUT_DEVICE_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The ItemInfoListener to register. + */ + public synchronized void + addAudioDeviceInfoListener(ItemInfoListener l) { + if(llAODI.isEmpty()) subscribe("AUDIO_OUTPUT_DEVICE_INFO"); + llAODI.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The ItemInfoListener to remove. + */ + public synchronized void + removeAudioDeviceInfoListener(ItemInfoListener l) { + boolean b = llAODI.remove(l); + if(b && llAODI.isEmpty()) unsubscribe("AUDIO_OUTPUT_DEVICE_INFO"); + } + + /** + * Registers the specified listener for receiving event messages. * Listeners can be removed regardless of the connection state. * @param l The BufferFillListener to register. */ @@ -564,6 +1254,94 @@ /** * Registers the specified listener for receiving event messages. * Listeners can be registered regardless of the connection state. + * @param l The FxSendCountListener to register. + */ + public synchronized void + addFxSendCountListener(FxSendCountListener l) { + if(llFSC.isEmpty()) subscribe("FX_SEND_COUNT"); + llFSC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The FxSendCountListener to remove. + */ + public synchronized void + removeFxSendCountListener(FxSendCountListener l) { + boolean b = llFSC.remove(l); + if(b && llFSC.isEmpty()) unsubscribe("FX_SEND_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The FxSendInfoListener to register. + */ + public synchronized void + addFxSendInfoListener(FxSendInfoListener l) { + if(llFSI.isEmpty()) subscribe("FX_SEND_INFO"); + llFSI.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The FxSendInfoListener to remove. + */ + public synchronized void + removeFxSendInfoListener(FxSendInfoListener l) { + boolean b = llFSI.remove(l); + if(b && llFSI.isEmpty()) unsubscribe("FX_SEND_INFO"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The ItemCountListener to register. + */ + public synchronized void + addMidiDeviceCountListener(ItemCountListener l) { + if(llMIDC.isEmpty()) subscribe("MIDI_INPUT_DEVICE_COUNT"); + llMIDC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The ItemCountListener to remove. + */ + public synchronized void + removeMidiDeviceCountListener(ItemCountListener l) { + boolean b = llMIDC.remove(l); + if(b && llMIDC.isEmpty()) unsubscribe("MIDI_INPUT_DEVICE_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The ItemInfoListener to register. + */ + public synchronized void + addMidiDeviceInfoListener(ItemInfoListener l) { + if(llMIDI.isEmpty()) subscribe("MIDI_INPUT_DEVICE_INFO"); + llMIDI.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The ItemInfoListener to remove. + */ + public synchronized void + removeMidiDeviceInfoListener(ItemInfoListener l) { + boolean b = llMIDI.remove(l); + if(b && llMIDI.isEmpty()) unsubscribe("MIDI_INPUT_DEVICE_INFO"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. * @param l The MiscellaneousListener to register. */ public synchronized void @@ -628,20 +1406,337 @@ } /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The TotalStreamCountListener to register. + */ + public synchronized void + addTotalStreamCountListener(TotalStreamCountListener l) { + if(llTSC.isEmpty()) subscribe("TOTAL_STREAM_COUNT"); + llTSC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The TotalStreamCountListener to remove. + */ + public synchronized void + removeTotalStreamCountListener(TotalStreamCountListener l) { + boolean b = llTSC.remove(l); + if(b && llTSC.isEmpty()) unsubscribe("TOTAL_STREAM_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The TotalVoiceCountListener to register. + */ + public synchronized void + addTotalVoiceCountListener(TotalVoiceCountListener l) { + if(llTVC.isEmpty()) subscribe("TOTAL_VOICE_COUNT"); + llTVC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The TotalVoiceCountListener to remove. + */ + public synchronized void + removeTotalVoiceCountListener(TotalVoiceCountListener l) { + boolean b = llTVC.remove(l); + if(b && llTVC.isEmpty()) unsubscribe("TOTAL_VOICE_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The ItemCountListener to register. + */ + public synchronized void + addMidiInstrumentMapCountListener(ItemCountListener l) { + if(llMIMC.isEmpty()) subscribe("MIDI_INSTRUMENT_MAP_COUNT"); + llMIMC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The ItemCountListener to remove. + */ + public synchronized void + removeMidiInstrumentMapCountListener(ItemCountListener l) { + boolean b = llMIMC.remove(l); + if(b && llMIMC.isEmpty()) unsubscribe("MIDI_INSTRUMENT_MAP_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The ItemInfoListener to register. + */ + public synchronized void + addMidiInstrumentMapInfoListener(ItemInfoListener l) { + if(llMIMI.isEmpty()) subscribe("MIDI_INSTRUMENT_MAP_INFO"); + llMIMI.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The ItemInfoListener to remove. + */ + public synchronized void + removeMidiInstrumentMapInfoListener(ItemInfoListener l) { + boolean b = llMIMI.remove(l); + if(b && llMIMI.isEmpty()) unsubscribe("MIDI_INSTRUMENT_MAP_INFO"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The MidiInstrumentCountListener to register. + */ + public synchronized void + addMidiInstrumentCountListener(MidiInstrumentCountListener l) { + if(llMIC.isEmpty()) subscribe("MIDI_INSTRUMENT_COUNT"); + llMIC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The MidiInstrumentCountListener to remove. + */ + public synchronized void + removeMidiInstrumentCountListener(MidiInstrumentCountListener l) { + boolean b = llMIC.remove(l); + if(b && llMIC.isEmpty()) unsubscribe("MIDI_INSTRUMENT_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The MidiInstrumentInfoListener to register. + */ + public synchronized void + addMidiInstrumentInfoListener(MidiInstrumentInfoListener l) { + if(llMII.isEmpty()) subscribe("MIDI_INSTRUMENT_INFO"); + llMII.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The MidiInstrumentInfoListener to remove. + */ + public synchronized void + removeMidiInstrumentInfoListener(MidiInstrumentInfoListener l) { + boolean b = llMII.remove(l); + if(b && llMII.isEmpty()) unsubscribe("MIDI_INSTRUMENT_INFO"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The DeviceMidiDataListener to register. + */ + public synchronized void + addDeviceMidiDataListener(DeviceMidiDataListener l) { + if(llDMD.isEmpty()) subscribe("DEVICE_MIDI"); + llDMD.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The DeviceMidiDataListener to remove. + */ + public synchronized void + removeDeviceMidiDataListener(DeviceMidiDataListener l) { + boolean b = llDMD.remove(l); + if(b && llDMD.isEmpty()) unsubscribe("DEVICE_MIDI"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The ChannelMidiDataListener to register. + */ + public synchronized void + addChannelMidiDataListener(ChannelMidiDataListener l) { + if(llCMD.isEmpty()) subscribe("CHANNEL_MIDI"); + llCMD.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The ChannelMidiDataListener to remove. + */ + public synchronized void + removeChannelMidiDataListener(ChannelMidiDataListener l) { + boolean b = llCMD.remove(l); + if(b && llCMD.isEmpty()) unsubscribe("CHANNEL_MIDI"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The InstrumentsDbListener to register. + */ + public synchronized void + addInstrumentsDbListener(InstrumentsDbListener l) { + if(llID.isEmpty()) { + subscribe("DB_INSTRUMENT_DIRECTORY_COUNT"); + subscribe("DB_INSTRUMENT_DIRECTORY_INFO"); + subscribe("DB_INSTRUMENT_COUNT"); + subscribe("DB_INSTRUMENT_INFO"); + subscribe("DB_INSTRUMENTS_JOB_INFO"); + } + llID.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The InstrumentsDbListener to remove. + */ + public synchronized void + removeInstrumentsDbListener(InstrumentsDbListener l) { + boolean b = llID.remove(l); + if(b && llID.isEmpty()) { + unsubscribe("DB_INSTRUMENT_DIRECTORY_COUNT"); + unsubscribe("DB_INSTRUMENT_DIRECTORY_INFO"); + unsubscribe("DB_INSTRUMENT_COUNT"); + unsubscribe("DB_INSTRUMENT_INFO"); + unsubscribe("DB_INSTRUMENTS_JOB_INFO"); + } + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The GlobalInfoListener to register. + */ + public synchronized void + addGlobalInfoListener(GlobalInfoListener l) { + if(llGI.isEmpty()) subscribe("GLOBAL_INFO"); + llGI.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The GlobalInfoListener to remove. + */ + public synchronized void + removeGlobalInfoListener(GlobalInfoListener l) { + boolean b = llGI.remove(l); + if(b && llGI.isEmpty()) unsubscribe("GLOBAL_INFO"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The EffectInstanceCountListener to register. + */ + public synchronized void + addEffectInstanceCountListener(EffectInstanceCountListener l) { + if(llEIC.isEmpty()) subscribe("EFFECT_INSTANCE_COUNT"); + llEIC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The EffectInstanceCountListener to remove. + */ + public synchronized void + removeEffectInstanceCountListener(EffectInstanceCountListener l) { + boolean b = llEIC.remove(l); + if(b && llEIC.isEmpty()) unsubscribe("EFFECT_INSTANCE_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The EffectInstanceInfoListener to register. + */ + public synchronized void + addEffectInstanceInfoListener(EffectInstanceInfoListener l) { + if(llEII.isEmpty()) subscribe("EFFECT_INSTANCE_INFO"); + llEII.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The EffectInstanceInfoListener to remove. + */ + public synchronized void + removeEffectInstanceInfoListener(EffectInstanceInfoListener l) { + boolean b = llEII.remove(l); + if(b && llEII.isEmpty()) unsubscribe("EFFECT_INSTANCE_INFO"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The SendEffectChainCountListener to register. + */ + public synchronized void + addSendEffectChainCountListener(SendEffectChainCountListener l) { + if(llSECC.isEmpty()) subscribe("SEND_EFFECT_CHAIN_COUNT"); + llSECC.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The SendEffectChainCountListener to remove. + */ + public synchronized void + removeSendEffectChainCountListener(SendEffectChainCountListener l) { + boolean b = llSECC.remove(l); + if(b && llSECC.isEmpty()) unsubscribe("SEND_EFFECT_CHAIN_COUNT"); + } + + /** + * Registers the specified listener for receiving event messages. + * Listeners can be registered regardless of the connection state. + * @param l The SendEffectChainInfoListener to register. + */ + public synchronized void + addSendEffectChainInfoListener(SendEffectChainInfoListener l) { + if(llSECI.isEmpty()) subscribe("SEND_EFFECT_CHAIN_INFO"); + llSECI.add(l); + } + + /** + * Removes the specified listener. + * Listeners can be removed regardless of the connection state. + * @param l The SendEffectChainInfoListener to remove. + */ + public synchronized void + removeSendEffectChainInfoListener(SendEffectChainInfoListener l) { + boolean b = llSECI.remove(l); + if(b && llSECI.isEmpty()) unsubscribe("SEND_EFFECT_CHAIN_INFO"); + } + + /** * Gets the number of all audio output drivers currently * available for the LinuxSampler instance. * @return The number of all audio output drivers currently - * available for the LinuxSampler instance. + * available for the LinuxSampler instance or -1 if in "print only" mode. * @throws IOException If some I/O error occurs. * @throws LscpException If LSCP protocol corruption occurs. * @throws LSException If some other error occurs. */ public synchronized int getAudioOutputDriverCount() throws IOException, LscpException, LSException { - verifyConnection(); - out.writeLine("GET AVAILABLE_AUDIO_OUTPUT_DRIVERS"); - String s = getSingleLineResultSet().getResult(); - return parseInt(s); + return retrieveInt("GET AVAILABLE_AUDIO_OUTPUT_DRIVERS"); } /** @@ -657,6 +1752,8 @@ public synchronized AudioOutputDriver[] getAudioOutputDrivers() throws IOException, LscpException, LSException { String[] drivers = getAudioOutputDriverNames(); + if(getPrintOnlyMode()) return null; + AudioOutputDriver[] aod = new AudioOutputDriver[drivers.length]; for(int i = 0; i < aod.length; i++) aod[i] = getAudioOutputDriverInfo(drivers[i]); @@ -678,13 +1775,14 @@ getAudioOutputDriverNames() throws IOException, LscpException, LSException { verifyConnection(); out.writeLine("LIST AVAILABLE_AUDIO_OUTPUT_DRIVERS"); + if(getPrintOnlyMode()) return null; return parseList(getSingleLineResultSet().getResult()); } /** * Gets detailed information about a specific audio output driver. * @param driverName The name of the audio output driver. - * + * @param depList An optional list of dependences parameters. * @return An AudioOutputDriver object containing * information about the specified audio output driver. * @@ -694,16 +1792,16 @@ * * @see #getAudioOutputDriverNames */ - private synchronized AudioOutputDriver - getAudioOutputDriverInfo(String driverName) throws IOException, LscpException, LSException { - verifyConnection(); - out.writeLine("GET AUDIO_OUTPUT_DRIVER INFO " + driverName); - ResultSet rs = getMultiLineResultSet(); - AudioOutputDriver aod = new AudioOutputDriver(rs.getMultiLineResult()); + public synchronized AudioOutputDriver + getAudioOutputDriverInfo(String driverName, Parameter... depList) + throws IOException, LscpException, LSException { + + AudioOutputDriver aod = new AudioOutputDriver(); + if(!retrieveInfo("GET AUDIO_OUTPUT_DRIVER INFO " + driverName, aod)) return null; aod.setName(driverName); for(String s : aod.getParameterNames()) - aod.addParameter(getAudioOutputDriverParameterInfo(driverName, s)); + aod.addParameter(getAudioOutputDriverParameterInfo(driverName, s, depList)); return aod; } @@ -737,10 +1835,13 @@ StringBuffer args = new StringBuffer(driver); args.append(' ').append(param); - for(Parameter p : deplist) + for(Parameter p : deplist) { + if(p == null || p.getName() == null || p.getValue() == null) continue; args.append(' ').append(p.getName()).append('=').append(p.getStringValue()); + } out.writeLine("GET AUDIO_OUTPUT_DRIVER_PARAMETER INFO " + args.toString()); + if(getPrintOnlyMode()) return null; ResultSet rs = getMultiLineResultSet(); @@ -754,21 +1855,25 @@ if(!multi) prm = new BoolParameter(lnS); else prm = new BoolListParameter(lnS); prm.setName(param); + prm.setValue(prm.getDefault()); return prm; case INT: if(!multi) prm = new IntParameter(lnS); else prm = new IntListParameter(lnS); prm.setName(param); + prm.setValue(prm.getDefault()); return prm; case FLOAT: if(!multi) prm = new FloatParameter(lnS); else prm = new FloatListParameter(lnS); prm.setName(param); + prm.setValue(prm.getDefault()); return prm; case STRING: if(!multi) prm = new StringParameter(lnS); else prm = new StringListParameter(lnS); prm.setName(param); + prm.setValue(prm.getDefault()); return prm; default: throw new LscpException(LscpI18n.getLogMsg("Client.unknownPrmType!")); } @@ -791,67 +1896,97 @@ createAudioOutputDevice(String aoDriver, Parameter... paramList) throws IOException, LSException, LscpException { - verifyConnection(); StringBuffer args = new StringBuffer(aoDriver); - for(Parameter p : paramList) + for(Parameter p : paramList) { + if(p == null || p.getName() == null || p.getValue() == null) continue; args.append(' ').append(p.getName()).append('=').append(p.getStringValue()); - - out.writeLine("CREATE AUDIO_OUTPUT_DEVICE " + args.toString()); - ResultSet rs = getEmptyResultSet(); - - return rs.getIndex(); + } + + return retrieveIndex("CREATE AUDIO_OUTPUT_DEVICE " + args.toString()); } /** * Destroys already created audio output device. - * @param deviceID The ID of the audio output device to be destroyed. + * @param deviceId The ID of the audio output device to be destroyed. * @throws IOException If some I/O error occurs. * @throws LSException If the destroying of the audio output device failed. * @throws LscpException If LSCP protocol corruption occurs. * @see #getAudioOutputDevices */ public synchronized void - destroyAudioOutputDevice(int deviceID) throws IOException, LSException, LscpException { - verifyConnection(); - out.writeLine("DESTROY AUDIO_OUTPUT_DEVICE " + deviceID); - ResultSet rs = getEmptyResultSet(); + destroyAudioOutputDevice(int deviceId) throws IOException, LSException, LscpException { + retrieveIndex("DESTROY AUDIO_OUTPUT_DEVICE " + deviceId); + } + + /** + * Enables/disables the specified audio output device. + * @param deviceId The ID of the audio output device to be enabled/disabled. + * @param enable If true the audio output device is enabled, + * else the device is disabled. + * @throws IOException If some I/O error occurs. + * @throws LSException If there is no audio output + * device with numerical ID deviceId. + * @throws LscpException If LSCP protocol corruption occurs. + */ + public void + enableAudioOutputDevice(int deviceId, boolean enable) + throws IOException, LSException, LscpException { + + setAudioOutputDeviceParameter(deviceId, new BoolParameter("ACTIVE", enable)); } /** * Gets the current number of all created audio output devices. - * @return The current number of all created audio output devices. + * @return The current number of all created audio output devices + * or -1 if in "print only" mode. * @throws IOException If some I/O error occurs. * @throws LscpException If LSCP protocol corruption occurs. * @throws LSException If some other error occurs. */ public synchronized int getAudioOutputDeviceCount() throws IOException, LscpException, LSException { - verifyConnection(); - out.writeLine("GET AUDIO_OUTPUT_DEVICES"); - String s = getSingleLineResultSet().getResult(); - return parseInt(s); + return retrieveInt("GET AUDIO_OUTPUT_DEVICES"); } /** + * Gets a list of all created audio output devices. + * @return An AudioOutputDevice array + * providing all created audio output devices. + * @throws IOException If some I/O error occurs. + * @throws LscpException If LSCP protocol corruption occurs. + * @throws LSException If some other error occurs. + */ + public synchronized AudioOutputDevice[] + getAudioOutputDevices() throws IOException, LscpException, LSException { + Integer[] idS = getAudioOutputDeviceIDs(); + if(getPrintOnlyMode()) return null; + + AudioOutputDevice[] devices = new AudioOutputDevice[idS.length]; + + for(int i = 0; i < devices.length; i++) + devices[i] = getAudioOutputDeviceInfo(idS[i]); + + return devices; + } + + /** * Gets a list of numerical IDs of all created audio output devices. - * @return An Integer array with numerical IDs of + * @return An Integer array providing the numerical IDs of * all created audio output devices. * @throws IOException If some I/O error occurs. * @throws LscpException If LSCP protocol corruption occurs. * @throws LSException If some other error occurs. */ public synchronized Integer[] - getAudioOutputDevices() throws IOException, LscpException, LSException { - verifyConnection(); - out.writeLine("LIST AUDIO_OUTPUT_DEVICES"); - return parseIntList(getSingleLineResultSet().getResult()); + getAudioOutputDeviceIDs() throws IOException, LscpException, LSException { + return getIntegerList("LIST AUDIO_OUTPUT_DEVICES"); } /** * Gets the current settings of a specific, already created audio output device. * - * @param deviceID Specifies the numerical ID of the audio output device. + * @param deviceId Specifies the numerical ID of the audio output device. * * @return An AudioOutputDevice instance containing information * about the specified device. @@ -859,20 +1994,22 @@ * @throws IOException If some I/O error occurs. * @throws LscpException If LSCP protocol corruption occurs. * @throws LSException If there is no audio output device - * with device id deviceID. + * with device id deviceId. * * @see #getAudioOutputDevices */ public synchronized AudioOutputDevice - getAudioOutputDeviceInfo(int deviceID) throws IOException, LscpException, LSException { + getAudioOutputDeviceInfo(int deviceId) throws IOException, LscpException, LSException { verifyConnection(); - out.writeLine("GET AUDIO_OUTPUT_DEVICE INFO " + deviceID); + out.writeLine("GET AUDIO_OUTPUT_DEVICE INFO " + deviceId); + if(getPrintOnlyMode()) return null; ResultSet rs = getMultiLineResultSet(); String[] lnS = rs.getMultiLineResult(); AudioOutputDevice aod = new AudioOutputDevice(); + aod.setDeviceId(deviceId); Parameter channels; Parameter samplerate; @@ -887,6 +2024,12 @@ s = s.substring("CHANNELS: ".length(), s.length()); channels.parseValue(s); aod.setChannelsParameter(channels); + int count = channels.getValue() > 0 ? channels.getValue() : 0; + AudioOutputChannel[] aoc = new AudioOutputChannel[count]; + for(int i = 0; i < count; i++) { + aoc[i] = getAudioOutputChannelInfo(deviceId, i); + } + aod.setAudioChannels(aoc); } else if(s.startsWith("SAMPLERATE: ")) { samplerate = (Parameter) getAudioOutputDriverParameterInfo(drv, "SAMPLERATE"); @@ -921,7 +2064,7 @@ /** * Alters a specific setting of a created audio output device. * - * @param deviceID The numerical ID of the audio output device. + * @param deviceId The numerical ID of the audio output device. * @param prm A Parameter instance containing the name of the parameter * and the new value for this parameter. * @@ -929,7 +2072,7 @@ * @throws LscpException If LSCP protocol corruption occurs. * @throws LSException If *