Package de.sciss.swingosc

Source Code of de.sciss.swingosc.ScopeView

/*
*  ScopeView.java
*  SwingOSC
*
*  Copyright (c) 2005-2011 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
*
*
*  Changelog:
*    22-Feb-05  created
*    29-Apr-07  fixed TCP mode, updated for NetUtil API changes
*/
package de.sciss.swingosc;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import javax.swing.JComponent;
import javax.swing.Timer;

import de.sciss.net.OSCClient;
import de.sciss.net.OSCListener;
import de.sciss.net.OSCMessage;
//import de.sciss.net.OSCPacket;
import de.sciss.net.OSCPacketCodec;

/**
* @version  0.53, 29-Apr-07
* @author  Hanns Holger Rutz
*/
public class ScopeView
extends JComponent
implements OSCListener, ActionListener
{
  // warning: while scsynth seems to have been updated to 64K buffers,
  // the /b_setn is only sent for sizes <= 8K for some reason!!
  private static final int  OSC_BUF_SIZE = 8192;
  private static final int  SLICE_SIZE  = (OSC_BUF_SIZE - 32) / 5; // 4bytes per float, 1byte per typetag : < 64K
 
  private float[]        vector    = null;
  private float[]        pntVector  = null;
  // note that since Graphics.drawPolyline is much
  // faster than Graphics2D.draw( GeneralPath ), we
  // use polies. to allow for some amount of antialiasing
  // both x and y coordinates are scaled up by a factor of 4
  // and the Graphics2D context will scale them down again
  private int[]        polyX, polyY;
  private int          size    = 0;
  private int          numFrames  = 0;
  private int          numChannels  = 0;
//  private int          sizeM1    = -1;
  private int          bufNum    = 0;
  private int          sliceID    = 0;
  private int          polySize  = 0;
  private int          polySizeC  = 0;

  private static final Color  colrFg    = Color.yellow;
//  private Color        colrGrid;
  private Color[]        colrWave  = new Color[ 0 ];
//  private boolean       antiAlias   = false;
  private final Stroke    strkPoly  = new BasicStroke( 4f );
  private int         recentWidth  = -1;
  private float        xZoom    = 1.0f;
  private float        yZoom    = 1.0f;
  private boolean        sah      = false;
  private boolean        overlay    = false;
  private boolean        lissajou  = false;
 
  private int          style    = 0;
 
  private boolean        isListening  = false;
  private InetSocketAddress  addr    = null;
  private String        protocol;
//  private DatagramChannel    dch      = null;
//  private OSCTransmitter    trns    = null;
//  private OSCReceiver      rcv      = null;
  private OSCClient      client    = null;
  private OSCMessage[]    msgBufGetN  = null;
  private int[]        msgBufOff  = null;

  private final Object    sync    = new Object();
 
  private final Timer      timer;
 
  public ScopeView()
  {
    super();
 
    setOpaque( true );
    setBackground( Color.black );
    setFocusable( true );
   
    addMouseListener( new MouseAdapter() {
      public void mousePressed( MouseEvent e )
      {
        requestFocus();
      }
    });
   
    timer = new Timer( 1000, this );
    timer.setRepeats( true );
  }
 
  public void setServer( String hostName, int port, String protocol )
  {
    synchronized( sync ) {
      final boolean wasListening = isListening();
      if( wasListening ) {
        stopListening();
      }
      if( hostName.equals( "127.0.0.1" ) || hostName.equals( "localhost" )) {
        try {
          addr  = new InetSocketAddress( InetAddress.getLocalHost(), port );
//          addr  = new InetSocketAddress( "192.168.2.106", port );
        }
        catch( UnknownHostException e1 ) {
          addr  = new InetSocketAddress( hostName, port );
        }
      } else {
        addr    = new InetSocketAddress( hostName, port );
      }
//System.err.println( "host = " + hostName + " => " + addr.getHostName() );
      this.protocol  = protocol;
      if( wasListening ) {
        startListening();
      }
    }
  }
 
  public void startListening()
  {
    synchronized( sync ) {
      if( !isListening ) {
        if( addr == null ) {
          throw new IllegalStateException( "Server has not been specified" );
        }
//        final Map map = new HashMap( 1 );
        try {
          client  = OSCClient.newUsing( protocol );
          client.setBufferSize( OSC_BUF_SIZE )// current scsynth versions!
//          client  = OSCClient.newUsing( protocol, 0, true );
//System.err.println( "size = " + size );
          if( size > 0 ) {
            setCustomDecoder();
          }
//client.dumpOSC( 1, System.out );
          client.setTarget( addr );
              client.start();
              client.addOSCListener( this );
          isListening = true;
          query();
          timer.restart();
//          System.out.println(" restarted ");
        }
        catch( IOException e1 ) {
          if( client != null ) {
            client.removeOSCListener( this );
            client.dispose();
            client = null;
          }
          msgBufGetN  = null;
          isListening  = false;
         
          System.out.println( e1 );
        }
      }
    }
  }
 
  public void stopListening()
  {
    synchronized( sync ) {
      if( isListening ) {
        timer.stop();
//System.out.println(" stopped ");
        client.removeOSCListener( this );
        msgBufGetN = null;
        try {
          client.stop();
        }
        catch( IOException e1 ) {
          System.out.println( e1 );
        }
        client.dispose();
        client = null;
        isListening  = false;
        vector    = null;
        pntVector  = null;
      }
    }
  }
 
  public boolean isListening()
  {
    return isListening;
  }
 
  public void setBuffer( int bufNum, int numFrames, int numChannels, float sampleRate )
  {
    synchronized( sync ) {
//System.out.println( "setBuffer "+bufNum+", "+numFrames+", "+numChannels + "; isListening? " + isListening );
      size      = numFrames * numChannels;
      vector       = new float[ size ];
      pntVector     = new float[ size ];
      this.bufNum    = bufNum;
      this.numFrames  = numFrames;
      this.numChannels= numChannels;
//      sizeM1      = size - 1;
      polyX      = new int[ numFrames << 1 ];
      polyY      = new int[ numFrames << 1 ];
      recentWidth    = -1// need to recalc X coords
     
      if( isListening ) {
        setCustomDecoder();
        query();
      }
    }
  }
 
  private void setCustomDecoder()
  {
//System.out.println( "setCustomDecoder" );
    final OSCSharedBufSetNMsg bufSetNMsg = new OSCSharedBufSetNMsg( vector );
    createMessages();
    client.setCodec( new OSCPacketCodec() {
      protected OSCMessage decodeMessage( String command, ByteBuffer b )
      throws IOException
      {
        if( command.equals( "/b_setn" )) {
//System.out.println( "/b_setn" );
          return bufSetNMsg.decodeSpecific( b );
        } else {
          return super.decodeMessage( command, b );
        }
      }
     
      protected int getMessageSize( OSCMessage msg )
            throws IOException
            {
        if( msg instanceof OSCSharedBufSetNMsg ) {
          return ((OSCSharedBufSetNMsg) msg).getSpecificSize();
        } else {
          return super.getMessageSize( msg );
        }
            }
    });
  }
 
  // sync: caller must be in synchronized( sync ) block!
  private void createMessages()
  {
    final int numSlices   = (size + SLICE_SIZE - 1) / SLICE_SIZE;
    if( numSlices == 0 ) return;
    msgBufGetN      = new OSCMessage[ numSlices ];
    msgBufOff      = new int[ numSlices ];
   
    for( int i = 0, off = 0; i < numSlices; i++, off += SLICE_SIZE ) {
      msgBufOff[ i ] = off;
      msgBufGetN[ i ] = new OSCMessage( "/b_getn", new Object[] {
          new Integer( bufNum ), new Integer( off ), new Integer( Math.min( SLICE_SIZE, size - off ))});
    }
   
    sliceID        = 0;
  }

//  // sync: call in event thread only
//  public void setVectorSize( int size )
//  {
//    synchronized( sync ) {
//      vector     = new float[ size ];
//      this.size  = size;
//      sizeM1    = size - 1;
//      polyX    = new int[ size ];
//      polyY    = new int[ size ];
//     
//      if( isListening ) {
//        final Map map = new HashMap( 1 );
//        map.put( "/b_setn", new OSCSharedBufSetNMsg( vector ));
//        rcv.setCustomMessageDecoders( map );
//
//        msgBufGetN = new OSCMessage( "/b_getn", new Object[] {
//            new Integer( bufNum ), new Integer( 0 ), new Integer( size )});
//      }
//    }
//  }
 
//  public void setBufNum( int bufNum )
//  {
//    synchronized( sync ) {
//      this.bufNum  = bufNum;
//      if( isListening ) {
//        msgBufGetN  = new OSCMessage( "/b_getn", new Object[] {
//            new Integer( bufNum ), new Integer( 0 ), new Integer( size )});
//      }
//    }
//  }
 
  public void setBufNum( int bufNum )
  {
    synchronized( sync ) {
      if( isListening ) {
        msgBufGetN   = null// don't query until we've got the buffer specs
        this.bufNum  = bufNum;
        try {
          client.send( new OSCMessage( "/b_query", new Object[] { new Integer( bufNum )}));
        }
        catch( IOException e1 ) {
          System.out.println( e1 );
        }
      } else {
        throw new IllegalStateException( "ScopeView.setBufNum : call startListening before!" );
      }
    }
  }
 
  public void setStyle( int style )
  {
    this.style  = style;
    overlay    = style > 0;
    lissajou    = style == 2;
    recentWidth  = -1;
    repaint();
  }
 
  public int getStyle()
  {
    return style;
  }
 
  public void setXZoom( float f )
  {
    xZoom    = f;
    recentWidth  = -1// triggers recalc
    repaint();
  }

  public void setYZoom( float f )
  {
    yZoom  = f;
    repaint();
  }
 
  public float getXZoom()
  {
    return xZoom;
  }

  public float getYZoom()
  {
    return yZoom;
  }

//  public void setAntiAliasing( boolean onOff )
//  {
//    antiAlias = onOff;
//    repaint();
//  }
// 
//  public boolean getAntiAliasing()
//  {
//    return antiAlias;
//  }
 
  // sync: call in event thread only
  public void setWaveColors( Color[] c )
  {
    colrWave = c;
    repaint();
  }
 
  public void setObjWaveColors( Object[] o )
  {
    final Color[] c = new Color[ o.length ];
   
    for( int i = 0; i < o.length; i++ ) {
      c[ i ] = (Color) o[ i ];
    }
   
    setWaveColors( c );
  }
 
//  public void setGridColor( Color c )
//  {
//    colrGrid  = c;
//    repaint();
//  }
 
  // sync: caller must be in synchronized( sync ) block!!
  private void query()
  {
    if( msgBufGetN != null ) {
      try {
// scsynth is too stupid to handle all requests at once
//        for( int i = 0; i < msgBufGetN.length; i++ ) {
//          if( msgBufOff[ i ] > polySizeC ) break;
          client.send( msgBufGetN[ sliceID ]);
//System.out.println( "Sending request " + msgBufGetN[ i ].getArg( 1 )+"; "+msgBufGetN[ i ].getArg( 2 ));
//        }
        sliceID = sliceID + 1;
        if( (sliceID == msgBufGetN.length) || (msgBufOff[ sliceID ] > polySizeC) ) {
          sliceID = 0;
        }
      }
      catch( IOException e1 ) {
        System.out.println( e1 );
      }
    }
    timer.restart();
  }

  public void paintComponent( Graphics g )
  {
    super.paintComponent( g );
   
    final Graphics2D    g2    = (Graphics2D) g;
    final int        w    = getWidth();
    final int        h    = getHeight();
    final AffineTransform  atOrig  = g2.getTransform();
    final Stroke      strkOrig= g2.getStroke();
    final float        sy;
    float          offY  = 0f;
    int            x, y;
 
    g2.setColor( getBackground() );
    g2.fillRect( 0, 0, w, h );
   
    // XXX this sync becomes superfluous
    // if setBuffer was deferred to the event
    // thread from messageReceived ...
    // which could improve performance a bit
    synchronized( sync ) {
      if( (pntVector == null) || (numChannels == 0) ) return;
     
      if( lissajou ) {
        // the interpretation of the xZoom value is completely
        // stupid in cocoa scope. in lissajou mode increasing zoom
        // will increase scale, while in normal mode it's the other way round
        final int offX  = w << 1;
        final float sx  = offX * xZoom;
        sah        = false;
        polySize     = numFrames;
        polySizeC    = polySize * numChannels;
        for( int i = 0, k = 0; i < polySize; i++, k += numChannels ) {
          x      = (int) (pntVector[ k ] * sx + offX);
          polyX[ i = x;
        }
        recentWidth   = w;
       
      } else if( w != recentWidth ) {  // have to recalc horiz. coord
  //      final float  sx = w * xZoom / sizeM1;
  //      final float  sx = (w << 2) * xZoom / sizeM1;
        final float  sx = 4 / xZoom;
  //      polySize   = Math.min( numFrames, (int) (sizeM1 / xZoom) + 1 );
        polySize   = Math.min( numFrames, (int) (w * xZoom) + 1 );
        polySizeC  = polySize * numChannels;
  //      sah      = sx > 3;
        sah      = sx > 12;
        if( sah ) {
          x = 0;
          for( int i = 0, j = 0; i < polySize; i++ ) {
            polyX[ j++ = x;
            x      = (int) (i * sx);
            polyX[ j++ = x;
          }
        } else {
          for( int i = 0; i < polySize; i++ ) {
            x      = (int) (i * sx);
            polyX[ i = x;
          }
        }
        recentWidth   = w;
     
     
      g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
     
      if( overlay ) {
        offY    = h * 0.5f;
        sy    = -(h << 1) * yZoom;
      } else {
  //      sy    = -h * yZoom / (numChannels << 1);
        sy    = -(h << 1) * yZoom / numChannels;
      }
     
      for( int ch = lissajou ? 1 : 0; ch < numChannels; ch++ ) {
        g2.setColor( colrWave.length > ch ? colrWave[ ch ] : colrFg );
        g2.setStroke( strkPoly );
        if( overlay ) {
          g2.translate( 0, offY );
        } else {
          offY = (float) (((ch << 1) + 1) * h) / (numChannels << 1);
          g2.translate( 0, offY );
        }
        g2.scale( 0.25f, 0.25f );
        if( sah ) { // sample-and-hold
          for( int i = 0, j = 0, k = ch; i < polySize; i++, k += numChannels ) {
            y      = (int) (pntVector[ k ] * sy);
            polyY[ j++ ] = y;
            polyY[ j++ ] = y;
          }
       
          g2.drawPolyline( polyX, polyY, polySize << 1 );
       
        } else {
          for( int i = 0, k = ch; i < polySize; i++, k += numChannels ) {
            y      = (int) (pntVector[ k ] * sy);
            polyY[ i ]   = y;
          }
       
          g2.drawPolyline( polyX, polyY, polySize );
        }
       
        g2.setTransform( atOrig );
        g2.setStroke( strkOrig );
      }
    }
  }

  // ------------- ActionListener interface -------------

  // called upon timeout
  public void actionPerformed( ActionEvent e )
  {
    synchronized( sync ) {
      if( isListening ) setBufNum( bufNum );
    }
  }
   
  // ------------- OSCListener interface -------------
  public void messageReceived( OSCMessage msg, SocketAddress addr, long when )
  {
    synchronized( sync ) {
      if( msg instanceof OSCSharedBufSetNMsg ) {
        final OSCSharedBufSetNMsg bufSetNMsg = (OSCSharedBufSetNMsg) msg;
        if( bufSetNMsg.getStartOffset() + bufSetNMsg.getNumSamples() >= polySizeC ) {
          System.arraycopy( vector, 0, pntVector, 0, size );
          repaint()// paint complete waveform
        }
        if( isListening ) query()// request next slice
      } else if( msg.getName().equals( "/b_info" )) {
//System.err.println( "/b_info" );
        try {
          final int msgBufNum = ((Number) msg.getArg( 0 )).intValue();
          if( msgBufNum != bufNum ) return;
          setBuffer( bufNum, ((Number) msg.getArg( 1 )).intValue(),
                    ((Number) msg.getArg( 2 )).intValue(),
                    ((Number) msg.getArg( 3 )).floatValue() );
        }
        catch( ClassCastException e1 ) {
          System.out.println( e1 );
        }
      }
    }
  }
}
TOP

Related Classes of de.sciss.swingosc.ScopeView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.