/*
* Synth.java
* JCollider
*
* Copyright (c) 2004-2010 Hanns Holger Rutz. All rights reserved.
*
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either
* version 2, june 1991 of the License, or (at your option) any later version.
*
* This software 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License (gpl.txt) along with this software; 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 , or visit http://www.sciss.de/jcollider
*
*
* JCollider is closely modelled after SuperCollider Language,
* often exhibiting a direct translation from Smalltalk to Java.
* SCLang is a software originally developed by James McCartney,
* which has become an Open Source project.
* See http://supercollider.sourceforge.net/ for details.
*
*
* Changelog:
* 04-Aug-05 created
* 02-Oct-05 removed all setGroup statements. to have the group
* set correctly, use a NodeWatcher instead
* 12-Jul-08 added get(n)(Msg) and grain constructors
*/
package de.sciss.jcollider;
import java.io.IOException;
import java.util.Enumeration;
import javax.swing.tree.TreeNode;
import de.sciss.net.OSCBundle;
import de.sciss.net.OSCMessage;
/**
* Mimics SCLang's Synth class,
* that is, it's a client side
* representation of a synth in the synthesis graph
*
* @warning this is a quick direct translation from SClang
* which is largely untested. before all methods have been
* thoroughly verified, excepted some of them to be wrong
* or behave different than expected. what certainly works
* is instantiation and new-messages
*
* @author Hanns Holger Rutz
* @version 0.33, 12-Jul-08
*/
public class Synth
extends Node
{
private final String defName;
// immediately sends
public Synth( String defName, Node target )
throws IOException
{
this( defName, null, null, target, kAddToHead );
}
// immediately sends
public Synth( String defName, String[] argNames, float[] argValues, Node target )
throws IOException
{
this( defName, argNames, argValues, target, kAddToHead );
}
// immediately sends
public Synth( String defName, String[] argNames, float[] argValues, Node target, int addAction )
throws IOException
{
this( defName, target.getServer(), target.getServer().nextNodeID() );
getServer().sendMsg( newMsg( target, argNames, argValues, addAction ));
}
// doesn't send
private Synth( String defName, Server server, int nodeID )
{
super( server, nodeID );
this.defName = defName;
}
public static void grain( String defName, Node target )
throws IOException
{
grain( defName, null, null, target, kAddToHead );
}
public static void grain( String defName, String[] argNames, float[] argValues, Node target )
throws IOException
{
grain( defName, argNames, argValues, target, kAddToHead );
}
public static void grain( String defName, String[] argNames, float[] argValues, Node target, int addAction )
throws IOException
{
final Synth s = new Synth( defName, target.getServer(), -1 );
s.getServer().sendMsg( s.newMsg( target, argNames, argValues, addAction ));
}
public String getDefName()
{
return defName;
}
public OSCMessage newMsg()
{
return newMsg( getServer().asTarget() );
}
public OSCMessage newMsg( Node target )
{
return newMsg( target, null, null );
}
public OSCMessage newMsg( Node target, String[] argNames, float[] argValues )
{
return newMsg( target, argNames, argValues, kAddToHead );
}
public OSCMessage newMsg( Node target, String[] argNames, float[] argValues, int addAction )
{
if( target == null ) target = getServer().getDefaultGroup();
// removed 02-oct-05
// this.setGroup( addAction == kAddToHead || addAction == kAddToTail ?
// (Group) target : target.getGroup() );
final int argNum = argNames == null ? 0 : argNames.length;
final Object[] allArgs = new Object[ argNum * 2 + 4 ];
allArgs[ 0 ] = getDefName();
allArgs[ 1 ] = new Integer( getNodeID() );
allArgs[ 2 ] = new Integer( addAction );
allArgs[ 3 ] = new Integer( target.getNodeID() );
for( int i = 0, j = 4; i < argNum; i++ ) {
allArgs[ j++ ] = argNames[ i ];
allArgs[ j++ ] = new Float( argValues[ i ]);
}
return new OSCMessage( "/s_new", allArgs );
}
public static Synth newPaused( String defName, String[] argNames, float[] argValues, Node target )
throws IOException
{
return Synth.newPaused( defName, argNames, argValues, target, kAddToHead );
}
public static Synth newPaused( String defName, String[] argNames, float[] argValues, Node target, int addAction )
throws IOException
{
final Synth synth = Synth.basicNew( defName, target.getServer() );
final OSCBundle bndl = new OSCBundle( 0.0 );
bndl.addPacket( synth.newMsg( target, argNames, argValues, addAction ));
bndl.addPacket( synth.runMsg( false ));
synth.getServer().sendBundle( bndl );
return synth;
}
// does not send (used for bundling)
public static Synth basicNew( String defName, Server server )
{
return Synth.basicNew( defName, server, server.nextNodeID() );
}
public static Synth basicNew( String defName, Server server, int nodeID )
{
return new Synth( defName, server, nodeID );
}
public static Synth after( Node aNode, String defName )
throws IOException
{
return Synth.after( aNode, defName, null, null );
}
public static Synth after( Node aNode, String defName, String[] argNames, float[] argValues )
throws IOException
{
return new Synth( defName, argNames, argValues, aNode, kAddAfter );
}
public static Synth before( Node aNode, String defName )
throws IOException
{
return Synth.before( aNode, defName, null, null );
}
public static Synth before( Node aNode, String defName, String[] argNames, float[] argValues )
throws IOException
{
return new Synth( defName, argNames, argValues, aNode, kAddBefore );
}
public static Synth head( Group aGroup, String defName )
throws IOException
{
return Synth.head( aGroup, defName, null, null );
}
public static Synth head( Node aGroup, String defName, String[] argNames, float[] argValues )
throws IOException
{
return new Synth( defName, argNames, argValues, aGroup, kAddToHead );
}
public static Synth tail( Group aGroup, String defName )
throws IOException
{
return Synth.tail( aGroup, defName, null, null );
}
public static Synth tail( Node aGroup, String defName, String[] argNames, float[] argValues )
throws IOException
{
return new Synth( defName, argNames, argValues, aGroup, kAddToTail );
}
public static Synth replace( Node nodeToReplace, String defName )
throws IOException
{
return Synth.replace( nodeToReplace, defName, null, null );
}
public static Synth replace( Node nodeToReplace, String defName, String[] argNames, float[] argValues )
throws IOException
{
return new Synth( defName, argNames, argValues, nodeToReplace, kAddReplace );
}
public OSCMessage addToHeadMsg( Group aGroup, String[] argNames, float[] argValues )
{
return newMsg( aGroup, argNames, argValues, kAddToHead );
}
public OSCMessage addToTailMsg( Group aGroup, String[] argNames, float[] argValues )
{
return newMsg( aGroup, argNames, argValues, kAddToTail );
}
public OSCMessage addAfterMsg( Node aNode, String[] argNames, float[] argValues )
{
return newMsg( aNode, argNames, argValues, kAddAfter );
}
public OSCMessage addBeforeMsg( Node aNode, String[] argNames, float[] argValues )
{
return newMsg( aNode, argNames, argValues, kAddBefore );
}
public OSCMessage addReplaceMsg( Node aNode, String[] argNames, float[] argValues )
{
return newMsg( aNode, argNames, argValues, kAddReplace );
}
/**
* Queries the current value of a synth control.
*
* @param index the index of the control to query
* @return the curresponding control value
* @throws IOException when an error occurs sending the message, or when
* a timeout or failure occurs with scsynth processing the message
*/
public float get( int index )
throws IOException
{
final OSCMessage getMsg = getMsg( index );
final OSCMessage replyMsg = getServer().sendMsgSync( getMsg, "/n_set", "/fail",
new int[] { 0, 1 }, new Object[] { new Integer( getNodeID() ), new Integer( index )},
new int[] { 0 }, new Object[] { getMsg.getName() }, 4f );
if( (replyMsg != null) && replyMsg.getName().equals( "/n_set" )) {
return ((Number) replyMsg.getArg( 2 )).floatValue();
} else {
throw new IOException( replyMsg == null ? "s_get timeout" : "s_get failed" );
}
}
/**
* Queries different current values of a synth control.
*
* @param indices the indices of the controls to query
* @return the curresponding control values, or null if
* a timeout or failure occurs with scsynth processing the message
* @throws IOException when an error occurs sending the message
*/
public float[] get( int[] indices )
throws IOException
{
final OSCMessage getMsg = getMsg( indices );
final int[] doneIndices = new int[ indices.length + 1 ];
final Object[] doneMatches = new Object[ indices.length + 1 ];
doneIndices[ 0 ] = 0;
doneMatches[ 0 ] = new Integer( getNodeID() );
for( int i = 1, j = 0, k = 1; j < indices.length; i++, j++, k += 2 ) {
doneIndices[ i ] = k;
doneMatches[ i ] = new Integer( indices[ j ]);
}
final OSCMessage replyMsg = getServer().sendMsgSync( getMsg, "/n_set", "/fail",
doneIndices, doneMatches,
new int[] { 0 }, new Object[] { getMsg.getName() }, 4f );
if( (replyMsg != null) && replyMsg.getName().equals( "/n_set" )) {
final float[] values = new float[ indices.length ];
for( int i = 2, j = 0; j < indices.length; i += 2, j++ ) {
values[ j ] = ((Number) replyMsg.getArg( i )).floatValue();
}
return values;
} else {
return null;
}
}
/**
* Queries the current value of a synth control.
*
* @param name the name of the control to query
* @return the curresponding control value
* @throws IOException when an error occurs sending the message, or when
* a timeout or failure occurs with scsynth processing the message
*/
public float get( String name )
throws IOException
{
final OSCMessage getMsg = getMsg( name );
final OSCMessage replyMsg = getServer().sendMsgSync( getMsg, "/n_set", "/fail",
new int[] { 0, 1 }, new Object[] { new Integer( getNodeID() ), name },
new int[] { 0 }, new Object[] { getMsg.getName() }, 4f );
if( (replyMsg != null) && replyMsg.getName().equals( "/n_set" )) {
return ((Number) replyMsg.getArg( 2 )).floatValue();
} else {
throw new IOException( replyMsg == null ? "s_get timeout" : "s_get failed" );
}
}
/**
* Queries different current values of a synth control.
*
* @param names the names of the controls to query
* @return the curresponding control values, or null if
* a timeout or failure occurs with scsynth processing the message
* @throws IOException when an error occurs sending the message
*/
public float[] get( String[] names )
throws IOException
{
final OSCMessage getMsg = getMsg( names );
final int[] doneIndices = new int[ names.length + 1 ];
final Object[] doneMatches = new Object[ names.length + 1 ];
doneIndices[ 0 ] = 0;
doneMatches[ 0 ] = new Integer( getNodeID() );
for( int i = 1, j = 0, k = 1; j < names.length; i++, j++, k += 2 ) {
doneIndices[ i ] = k;
doneMatches[ i ] = names[ j ];
}
final OSCMessage replyMsg = getServer().sendMsgSync( getMsg, "/n_set", "/fail",
doneIndices, doneMatches,
new int[] { 0 }, new Object[] { getMsg.getName() }, 4f );
if( (replyMsg != null) && replyMsg.getName().equals( "/n_set" )) {
final float[] values = new float[ names.length ];
for( int i = 2, j = 0; j < names.length; i += 2, j++ ) {
values[ j ] = ((Number) replyMsg.getArg( i )).floatValue();
}
return values;
} else {
return null;
}
}
public OSCMessage getMsg( int index )
{
return new OSCMessage( "/s_get", new Object[] { new Integer( getNodeID() ), new Integer( index )});
}
public OSCMessage getMsg( int[] indices )
{
final Object[] args = new Object[ indices.length + 1 ];
args[ 0 ] = new Integer( getNodeID() );
for( int i = 1, j = 0; j < indices.length; i++, j++ ) {
args[ i ] = new Integer( indices[ j ]);
}
return new OSCMessage( "/s_get", args );
}
public OSCMessage getMsg( String name )
{
return new OSCMessage( "/s_get", new Object[] { new Integer( getNodeID() ), name });
}
public OSCMessage getMsg( String[] names )
{
final Object[] args = new Object[ names.length + 1 ];
args[ 0 ] = new Integer( getNodeID() );
System.arraycopy( names, 0, args, 1, names.length );
return new OSCMessage( "/s_get", args );
}
/**
* Queries a range of current values of the synth's controls.
*
* @param index the start index of the controls to query
* @param count the number of successive controls to query
* @return the curresponding control values or null, when
* a timeout or failure occurs with scsynth processing the message
* @throws IOException when an error occurs sending the message
*/
public float[] getn( int index, int count )
throws IOException
{
final OSCMessage getnMsg = getnMsg( index, count );
final OSCMessage replyMsg = getServer().sendMsgSync( getnMsg, "/n_setn", "/fail",
new int[] { 0, 1, 2 }, new Object[] { new Integer( getNodeID() ), new Integer( index ), new Integer( count )},
new int[] { 0 }, new Object[] { getnMsg.getName() }, 4f );
if( (replyMsg != null) && replyMsg.getName().equals( "/n_setn" )) {
final float[] values = new float[ count ];
for( int i = 3, j = 0; j < count; j++ ) {
values[ j ] = ((Number) replyMsg.getArg( i )).floatValue();
}
return values;
} else {
return null;
}
}
/**
* Queries a range of current values of the synth's controls.
*
* @param name the name of the first control to query
* @param count the number of successive controls to query
* @return the curresponding control values or null, when
* a timeout or failure occurs with scsynth processing the message
* @throws IOException when an error occurs sending the message
*/
public float[] getn( String name, int count )
throws IOException
{
final OSCMessage getnMsg = getnMsg( name, count );
final OSCMessage replyMsg = getServer().sendMsgSync( getnMsg, "/n_setn", "/fail",
new int[] { 0, 1, 2 }, new Object[] { new Integer( getNodeID() ), new Integer( name ), new Integer( count )},
new int[] { 0 }, new Object[] { getnMsg.getName() }, 4f );
if( (replyMsg != null) && replyMsg.getName().equals( "/n_setn" )) {
final float[] values = new float[ count ];
for( int i = 3, j = 0; j < count; j++ ) {
values[ j ] = ((Number) replyMsg.getArg( i )).floatValue();
}
return values;
} else {
return null;
}
}
/**
* Queries different ranges of current values of the synth's controls.
*
* @param indices the start indices of the controls to query
* @param counts for each start index, the number of successive controls to query
* @return the curresponding control values, or null if
* a timeout or failure occurs with scsynth processing the message
* @throws IOException when an error occurs sending the message
*/
public float[] getn( int[] indices, int[] counts )
throws IOException
{
// getnMsg() checks indices.length versus counts.length already
final OSCMessage getnMsg = getnMsg( indices, counts );
final int[] doneIndices = new int[ (indices.length << 1) + 1 ];
final Object[] doneMatches = new Object[ (indices.length << 1) + 1 ];
int numValues = 0;
doneIndices[ 0 ] = 0;
doneMatches[ 0 ] = new Integer( getNodeID() );
for( int i = 1, j = 0, k = 1; j < indices.length; j++ ) {
doneIndices[ i ] = k;
doneMatches[ i ] = new Integer( indices[ j ]);
i++;
k++;
doneIndices[ i ] = k;
doneMatches[ i ] = new Integer( counts[ j ]);
i++;
k += counts[ j ] + 1;
numValues += counts[ j ];
}
final OSCMessage replyMsg = getServer().sendMsgSync( getnMsg, "/n_setn", "/fail",
doneIndices, doneMatches,
new int[] { 0 }, new Object[] { getnMsg.getName() }, 4f );
if( (replyMsg != null) && replyMsg.getName().equals( "/n_setn" )) {
final float[] values = new float[ numValues ];
for( int i = 3, j = 0, k = 0; j < indices.length; i += 2, j++ ) {
for( int m = 0; m < counts[ j ]; i++, k++, m++ ) {
values[ k ] = ((Number) replyMsg.getArg( i )).floatValue();
}
}
return values;
} else {
return null;
}
}
/**
* Queries different ranges of current values of the synth's controls.
*
* @param names the start names of the controls to query
* @param counts for each start name, the number of successive controls to query
* @return the curresponding control values, or null if
* a timeout or failure occurs with scsynth processing the message
* @throws IOException when an error occurs sending the message
*/
public float[] getn( String[] names, int[] counts )
throws IOException
{
// getnMsg() checks indices.length versus counts.length already
final OSCMessage getnMsg = getnMsg( names, counts );
final int[] doneIndices = new int[ (names.length << 1) + 1 ];
final Object[] doneMatches = new Object[ (names.length << 1) + 1 ];
int numValues = 0;
doneIndices[ 0 ] = 0;
doneMatches[ 0 ] = new Integer( getNodeID() );
for( int i = 1, j = 0, k = 1; j < names.length; j++ ) {
doneIndices[ i ] = k;
doneMatches[ i ] = new Integer( names[ j ]);
i++;
k++;
doneIndices[ i ] = k;
doneMatches[ i ] = new Integer( counts[ j ]);
i++;
k += counts[ j ] + 1;
numValues += counts[ j ];
}
final OSCMessage replyMsg = getServer().sendMsgSync( getnMsg, "/n_setn", "/fail",
doneIndices, doneMatches,
new int[] { 0 }, new Object[] { getnMsg.getName() }, 4f );
if( (replyMsg != null) && replyMsg.getName().equals( "/n_setn" )) {
final float[] values = new float[ numValues ];
for( int i = 3, j = 0, k = 0; j < names.length; i += 2, j++ ) {
for( int m = 0; m < counts[ j ]; i++, k++, m++ ) {
values[ k ] = ((Number) replyMsg.getArg( i )).floatValue();
}
}
return values;
} else {
return null;
}
}
public OSCMessage getnMsg( int index, int count )
{
return new OSCMessage( "/s_getn", new Object[] { new Integer( getNodeID() ),
new Integer( index ), new Integer( count )});
}
public OSCMessage getnMsg( int[] indices, int[] counts )
{
if( indices.length != counts.length ) throw new IllegalArgumentException( "# of indices must match # of counts" );
final Object[] args = new Object[ (indices.length << 1) + 1 ];
args[ 0 ] = new Integer( getNodeID() );
for( int i = 1, j = 0; j < indices.length; j++ ) {
args[ i++ ] = new Integer( indices[ j ]);
args[ i++ ] = new Integer( counts[ j ]);
}
return new OSCMessage( "/s_getn", args );
}
public OSCMessage getnMsg( String name, int count )
{
return new OSCMessage( "/s_getn", new Object[] { new Integer( getNodeID() ),
name, new Integer( count )});
}
public OSCMessage getnMsg( String[] names, int[] counts )
{
if( names.length != counts.length ) throw new IllegalArgumentException( "# of names must match # of counts" );
final Object[] args = new Object[ (names.length << 1) + 1 ];
args[ 0 ] = new Integer( getNodeID() );
for( int i = 1, j = 0; j < names.length; j++ ) {
args[ i++ ] = names[ j ];
args[ i++ ] = new Integer( counts[ j ]);
}
return new OSCMessage( "/s_getn", args );
}
public String toString()
{
if( getName() == null ) {
return( "Synth(" + getNodeID() + ",\"" + defName + "\")" );
} else {
return( "Synth::" + getName() + "(" + getNodeID() + ",\"" + defName + "\")" );
}
}
// -------------- TreeNode interface --------------
public TreeNode getChildAt( int childIndex )
{
return null;
}
public int getChildCount()
{
return 0;
}
public int getIndex( TreeNode node )
{
return -1;
}
public boolean getAllowsChildren()
{
return false;
}
public boolean isLeaf()
{
return true;
}
public Enumeration children()
{
return null; // XXX allowed?
}
}