/* * OSCMessage.java * de.sciss.net (NetUtil) * * Copyright (c) 2004-2008 Hanns Holger Rutz. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de * * * Changelog: * 25-Jan-05 created from de.sciss.meloncillo.net.OSCMessage * 22-Feb-05 new argument type OSCPacket allowed : in this case, * the packet is append as a OSC-blob which is very * convenient for SuperCollider which uses a lot of * optional completion-messages. * 26-May-05 moved to de.sciss.net package * 21-Jun-05 extended javadoc, NO_ARGS * 04-Sep-05 message argument class byte[] added (encoded as blob) * 09-Sep-05 fixed blob byte boundary alignment ; fixed getSize() method for blob args * 11-Nov-05 fix in blob decode * 28-Apr-07 moved codec stuff to OSCPacketCodec */ package de.sciss.net; import java.io.IOException; import java.nio.ByteBuffer; /** * Class for decoding OSC messages from * received datagrams or encoding OSC message * for sending to a target socket. See <A HREF="http://opensoundcontrol.org/spec-1_0">opensoundcontrol.org/spec-1_0</A> for * the specification of the message format. * </P><P> * Here is an example: * <pre> * DatagramChannel dch = null; * * final ByteBuffer buf = ByteBuffer.allocateDirect( 1024 ); * final SocketAddress addr = new InetSocketAddress( "localhost", 57110 ); * final Random rnd = new Random( System.currentTimeMillis() ); * * try { * dch = DatagramChannel.open(); * dch.configureBlocking( true ); * new OSCMessage( "/s_new", new Object[] { "default", * new Integer( 1001 ), new Integer( 1 ), new Integer( 0 ), * "out", new Integer( 0 ), "freq", new Float( 0 ), "amp", new Float( 0.1f )} * ).encode( buf ); * buf.flip(); * dch.send( buf, addr ); * * for( int i = 0; i < 11; i++ ) { * buf.clear(); * // no schoenheitsprize * new OSCMessage( "/n_set", new Object[] { new Integer( 1001 ), * "freq", new Float( 333 * Math.pow( 2, rnd.nextInt( 12 ) / 12.0f ))} * ).encode( buf ); * buf.flip(); * dch.send( buf, addr ); * Thread.currentThread().sleep( 300 ); * } * buf.clear(); * new OSCMessage( "/n_free", new Object[] { new Integer( 1001 )}).encode( buf ); * buf.flip(); * dch.send( buf, addr ); * } * catch( InterruptedException e1 ) {} * catch( IOException e2 ) { * System.err.println( e2.getLocalizedMessage() ); * } * finally { * if( dch != null ) { * try { * dch.close(); * } * catch( IOException e4 ) {}; * } * } * </pre> * * Note that this example uses the old way of sending messages. * A easier way is to create an <code>OSCTransmitter</code> which * handles the byte buffer for you. See the <code>OSCReceiver</code> doc * for an example using a dedicated transmitter. * * @author Hanns Holger Rutz * @version 0.33, 28-Apr-07 * * @see OSCReceiver */ public class OSCMessage extends OSCPacket { private Object[] args; private String name; /** * Shorthand to pass to the constructor * if you want to create an OSC message * which doesn't contain any arguments. * Note: alternatively you can use the * constructor <code>new OSCMessage( String )</code>. * * @see #OSCMessage( String ) */ public static final Object[] NO_ARGS = new Object[0]; /** * Creates a generic OSC message * with no arguments. * * @param name the OSC command, like "/s_new" */ public OSCMessage( String name ) { super(); this.name = name; this.args = NO_ARGS; } /** * Creates a generic OSC message * from Primitive arguments. * * @param name the OSC command, like "/s_new" * @param args array of arguments which are simply * assembled. Supported types are <code>Integer</code>, * <code>Long</code>, <code>Float</code>, <code>Double</code>, * <code>String</code>, furthermore <code>byte[]</code> and <code>OSCPacket</code> (both of which * are written as a blob). Note that in a future version of NetUtil, special codecs * will allow customization of the way classes are encoded. */ public OSCMessage( String name, Object[] args ) { super(); this.name = name; this.args = args; } /** * Returns the OSC command of this message * * @return the message's command, e.g. "/synced" etc. */ public String getName() { return name; } /** * Returns the number of arguments of the message. * * @return the number of typed arguments in the message. * e.g. for [ "/n_go", 1001, 0, -1, -1, 0 ] it returns 5. */ public int getArgCount() { return args.length; } /** * Returns the argument at the given index. * See <code>decodeMessage()</code> for information about the * used java classes. The most fail-safe way to handle numeric arguments * is to assume <code>Number</code> instead of a particular number subclass. * To read a primitive <code>int</code>, the recommended code is * <code>((Number) msg.getArg( index )).intValue()</code>, which will * work with any of <code>Integer</code>, <code>Long</code>, <code>Float</code>, <code>Double</code>. * * @param index index of the argument, beginning at zero, * must be less than <code>getArgCount()</code> * * @return the primitive type (<code>Integer</code>, <code>Float</code>, <code>String</code> etc.) argument at * the given index. * e.g. for [ "/n_go", 1001, 0, -1, -1, 0 ], requesting index * 0 would return <code>new Integer( 1001 )</code>. * * @see #getArgCount() * @see #decodeMessage( String, ByteBuffer ) * @see Number#intValue() */ public Object getArg( int index ) { return args[ index ]; } /** * Creates a new message with arguments decoded * from the ByteBuffer, using the default codec. Usually you call * <code>decode</code> from the <code>OSCPacket</code> * superclass or directly from the <code>OSCPacketCodec</code>. * * @param b ByteBuffer pointing right at * the beginning of the type * declaration section of the * OSC message, i.e. the name * was skipped before. * * @return new OSC message representing * the received message described * by the ByteBuffer. * * @throws IOException in case some of the * reading or decoding procedures failed. * @throws IllegalArgumentException occurs in some cases of buffer underflow * @see OSCPacketCodec#decodeMessage( String, ByteBuffer ) */ public static OSCMessage decodeMessage( String command, ByteBuffer b ) throws IOException { return OSCPacketCodec.getDefaultCodec().decodeMessage( command, b ); } }