/* * Copyright (C) 2004-2014, C. Ramakrishnan / Illposed Software. * All rights reserved. * * This code is licensed under the BSD 3-Clause license. * See file LICENSE (or LICENSE.html) for more information. */ package com.illposed.osc; import com.illposed.osc.utility.OSCByteArrayToJavaConverter; import com.illposed.osc.utility.OSCPacketDispatcher; import com.illposed.osc.utility.OSCPatternAddressSelector; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.nio.charset.Charset; /** * OSCPortIn is the class that listens for OSC messages. * * An example:<br> * (loosely based on {com.illposed.osc.OSCPortTest#testReceiving()}) * <blockquote><pre>{@code * receiver = new OSCPortIn(OSCPort.DEFAULT_SC_OSC_PORT()); * OSCListener listener = new OSCListener() { * public void acceptMessage(java.util.Date time, OSCMessage message) { * System.out.println("Message received!"); * } * }; * receiver.addListener("/message/receiving", listener); * receiver.startListening(); * }</pre></blockquote> * * Then, using a program such as SuperCollider or sendOSC, send a message * to this computer, port {@link #DEFAULT_SC_OSC_PORT}, * with the address "/message/receiving". * * @author Chandrasekhar Ramakrishnan */ public class OSCPortIn extends OSCPort implements Runnable { /** state for listening */ private boolean listening; private final OSCByteArrayToJavaConverter converter; private final OSCPacketDispatcher dispatcher; /** * Create an OSCPort that listens using a specified socket. * @param socket DatagramSocket to listen on. */ public OSCPortIn(DatagramSocket socket) { super(socket, socket.getLocalPort()); this.converter = new OSCByteArrayToJavaConverter(); this.dispatcher = new OSCPacketDispatcher(); } /** * Create an OSCPort that listens on the specified port. * Strings will be decoded using the systems default character set. * @param port UDP port to listen on. * @throws SocketException if the port number is invalid, * or there is already a socket listening on it */ public OSCPortIn(int port) throws SocketException { this(new DatagramSocket(port)); } /** * Create an OSCPort that listens on the specified port, * and decodes strings with a specific character set. * @param port UDP port to listen on. * @param charset how to decode strings read from incoming packages. * This includes message addresses and string parameters. * @throws SocketException if the port number is invalid, * or there is already a socket listening on it */ public OSCPortIn(int port, Charset charset) throws SocketException { this(port); this.converter.setCharset(charset); } /** * Buffers were 1500 bytes in size, but were * increased to 1536, as this is a common MTU. */ private static final int BUFFER_SIZE = 1536; /** * Run the loop that listens for OSC on a socket until * {@link #isListening()} becomes false. * @see java.lang.Runnable#run() */ public void run() { byte[] buffer = new byte[BUFFER_SIZE]; DatagramPacket packet = new DatagramPacket(buffer, BUFFER_SIZE); DatagramSocket socket = getSocket(); while (listening) { try { try { socket.receive(packet); } catch (SocketException ex) { if (listening) { throw ex; } else { // if we closed the socket while receiving data, // the exception is expected/normal, so we hide it continue; } } OSCPacket oscPacket = converter.convert(buffer, packet.getLength()); dispatcher.dispatchPacket(oscPacket); } catch (IOException e) { e.printStackTrace(); // XXX This may not be a good idea, as this could easily lead to a never ending series of exceptions thrown (due to the non-exited while loop), and because the user of the lib may want to handle this case himself } } } /** * Start listening for incoming OSCPackets */ public void startListening() { listening = true; Thread thread = new Thread(this); // The JVM exits when the only threads running are all daemon threads. thread.setDaemon(true); thread.start(); } /** * Stop listening for incoming OSCPackets */ public void stopListening() { listening = false; } /** * Am I listening for packets? * @return true if this port is in listening mode */ public boolean isListening() { return listening; } /** * Registers a listener that will be notified of incoming messages, * if their address matches the given pattern. * * @param addressSelector either a fixed address like "/sc/mixer/volume", * or a selector pattern (a mix between wildcards and regex) * like "/??/mixer/*", see {@link OSCPatternAddressSelector} for details * @param listener will be notified of incoming packets, if they match */ public void addListener(String addressSelector, OSCListener listener) { this.addListener(new OSCPatternAddressSelector(addressSelector), listener); } /** * Registers a listener that will be notified of incoming messages, * if their address matches the given selector. * @param addressSelector a custom address selector * @param listener will be notified of incoming packets, if they match */ public void addListener(AddressSelector addressSelector, OSCListener listener) { dispatcher.addListener(addressSelector, listener); } }