Package de.sciss.meloncillo.transmitter

Source Code of de.sciss.meloncillo.transmitter.SimpleTransmitterEditor

/*
*  SimpleTransmitterEditor.java
*  Meloncillo
*
*  Copyright (c) 2004-2008 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:
*    14-Jun-04   supports cursor info
*    25-Jul-04   frameBuf mit leerem Array initialisiert, bugfix in showCursorInfo
*    01-Aug-04   loadFrames moved from init to startListening
*    02-Sep-04  commented
*/

package de.sciss.meloncillo.transmitter;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JPanel;

import de.sciss.app.AbstractApplication;
import de.sciss.app.DynamicAncestorAdapter;
import de.sciss.app.DynamicListening;
import de.sciss.gui.TimeFormat;
import de.sciss.gui.VectorSpace;
import de.sciss.io.Span;
import de.sciss.meloncillo.Main;
import de.sciss.meloncillo.gui.ObserverPalette;
import de.sciss.meloncillo.gui.ToolActionEvent;
import de.sciss.meloncillo.gui.ToolActionListener;
import de.sciss.meloncillo.gui.VectorDisplay;
import de.sciss.meloncillo.gui.VectorEditor;
import de.sciss.meloncillo.gui.VirtualSurface;
import de.sciss.meloncillo.io.DecimatedTrail;
import de.sciss.meloncillo.io.DecimationInfo;
import de.sciss.meloncillo.session.Session;
import de.sciss.meloncillo.session.SessionCollection;
import de.sciss.meloncillo.timeline.TimelineEvent;
import de.sciss.meloncillo.timeline.TimelineListener;

/**
*  An editor suitable for <code>SimpleTransmitter</code>s.
*  Name edits are left to the
<code>ObserverPalette</code>. This simply
*  provides two <code>VectorEditor</code>s for
*  the x and y trajectory coordinates respectively.
*  Copy and paste functionality is provided by the TimelineFrame
*  but could go here some day.
*
@author    Hanns Holger Rutz
@version  0.75, 10-Jun-08
*
@see    SimpleTransmitter
*
*  @todo    disposal not explicitely handled.
*        MouseMotionListener is invoked too early sometimes, i.e. before frameBuf has
*        been updated.
*
*  @todo    BUG: when drawing directly into the vector editors,
*        writing out the new trajectories will shift the contents
*        a bit to the left each time
*/
public class SimpleTransmitterEditor
extends AbstractTransmitterEditor
implements ToolActionListener, VectorDisplay.Listener, TimelineListener, DynamicListening
{
  private static Vector        collTransmitterTypes;

  private final VectorEditor      xEditor;
  private final VectorEditor      yEditor;
 
  private final float[][]        frameBuf    = new float[2][0];
  private double            rate;
  private DecimationInfo        info      = null;

  private ObserverPalette        observer    = null;
  private final MouseMotionListener  cursorListener;
  private final String[]        cursorInfo    = new String[3];
  private final Object[]        msgArgs      = new Object[2];
  private final TimeFormat      msgCursorTime;
  private final MessageFormat      msgCursorX;
  private final MessageFormat      msgCursorY;
  private final SessionCollection.Listener  transmittersListener;

  static {
    collTransmitterTypes = new Vector( 1 );
    collTransmitterTypes.addElement( SimpleTransmitter.class );
  }

  /**
   *  Constructs a new transmitter editor with empty display.
   *  Attach the transmitter using <code>init</code>.
   */
  public SimpleTransmitterEditor()
  {
    super();
   
    final Container          c    = this;
    JPanel              padPanel1, padPanel2;
    final de.sciss.app.Application  app    = AbstractApplication.getApplication();
   
    msgCursorTime  = new TimeFormat( 0, app.getResourceString( "simpleTrnsEditTimePrefix" ), null, 3, Locale.US );
    msgCursorX    = new MessageFormat( app.getResourceString( "simpleTrnsEditXMsg" ), Locale.US );   // XXX
    msgCursorY    = new MessageFormat( app.getResourceString( "simpleTrnsEditYMsg" ), Locale.US );   // XXX

//    setOpaque( false );

    xEditor = new VectorEditor();
    xEditor.setSpace( null, VectorSpace.createLinSpace( 0.0, 1.0, 0.0, 1.0,
                app.getResourceString( "labelX" ), null, null, null ));
    xEditor.setFillArea( false );
    xEditor.addListener( this );    // we don't use DynamicListening because of being only rendered in the JTable
    padPanel1 = new JPanel();
    padPanel1.setLayout( new BorderLayout() );
    padPanel1.add( BorderLayout.CENTER, xEditor );
    padPanel1.setBorder( BorderFactory.createMatteBorder( 2, 0, 2, 0, Color.white ));   // top left bottom right
//    padPanel1.setOpaque( false );

    yEditor = new VectorEditor();
    yEditor.setSpace( null, VectorSpace.createLinSpace( 0.0, 1.0, 0.0, 1.0,
                app.getResourceString( "labelY" ), null, null, null ));
    yEditor.setFillArea( false );
    yEditor.addListener( this );    // we don't use DynamicListening because of being only rendered in the JTable
    padPanel2 = new JPanel();
    padPanel2.setLayout( new BorderLayout() );
    padPanel2.add( BorderLayout.CENTER, yEditor );
    padPanel2.setBorder( BorderFactory.createMatteBorder( 2, 0, 4, 0, Color.white ));
//    padPanel2.setOpaque( false );

    c.setLayout( new BoxLayout( c, BoxLayout.Y_AXIS ));
    c.add( padPanel1 );
    c.add( padPanel2 );

    // --- Listener ---
        new DynamicAncestorAdapter( this ).addTo( this );
    this.addComponentListener( new ComponentAdapter() {
      public void componentResized( ComponentEvent e )
      {
        loadFrames( true );
      }
    });

    cursorListener = new MouseMotionAdapter() {
      public void mouseMoved( MouseEvent e )
      {
        showCursorInfo( ((VirtualSurface) e.getSource()).screenToVirtual( e.getPoint() ));
      }

      public void mouseDragged( MouseEvent e )
      {
        showCursorInfo( ((VirtualSurface) e.getSource()).screenToVirtual( e.getPoint() ));
      }
    };
   
    transmittersListener = new SessionCollection.Listener() {
      public void sessionCollectionChanged( SessionCollection.Event e ) {}  // XXX could dispose
     
      public void sessionObjectChanged( SessionCollection.Event e )
      {
        if( e.collectionContains( trns ) && e.getModificationType() == Transmitter.OWNER_TRAJ ) {
          loadFrames( false );
        }
      }

      public void sessionObjectMapChanged( SessionCollection.Event e ) {}
    };
   }

  /**
   *  @synchronization  Caller should have sync on timeline, tc and mte?
   */
  public void init( Main root, Session doc, Transmitter trns )
  {
    super.init( root, doc, trns );
    observer = (ObserverPalette) root.getComponent( Main.COMP_OBSERVER );
  }

  public java.util.List getHandledTransmitters()
  {
    return collTransmitterTypes;
  }

  // Sync: syncs to tl / tc / mte
    private void loadFrames( boolean justBecauseOfResize )
    {
        Span      span;
//    AudioTrail    at    = trns.getTrackEditor();
        final DecimatedTrail  dt  = trns.getDecimatedWaveTrail();
       
        if( trns != null ) {
      if( !doc.bird.attemptShared( Session.DOOR_TIMETRNSMTE, 200 )) return;
      try {
                span = doc.timeline.getVisibleSpan();
        // wir fordern eine subsample version des zeitausschnitts
        // an, die mindestens 3/2 frames der aktuellen darstellungsbreite
        // enthaelt. dies ist ein guter kompromiss zwischen
        // darstellungsgenauigkeit und -geschwindigkeit
        rate = doc.timeline.getRate();
        info = dt.getBestSubsample( span, getWidth() * 3 / 2 );
        if( info.sublength != frameBuf[0].length ) {
          frameBuf[0] = new float[(int) info.sublength];
          frameBuf[1] = new float[(int) info.sublength];
        } else {
          if( justBecauseOfResize ) return;   // info.sublength didn't change
        }
// EEE
//        try {
//          dt.read( info, frameBuf, 0 );
//        }
//        catch( IOException e1 ) {
//          System.err.println( e1.getLocalizedMessage() );
//          info = null;
//        }
        xEditor.setVector( null, frameBuf[0] );
        yEditor.setVector( null, frameBuf[1] );
       
            }
      finally {
        doc.bird.releaseShared( Session.DOOR_TIMETRNSMTE );
      }
        }
    }

  private void showCursorInfo( Point2D pt )
  {
    int x;
 
    if( observer != null && observer.isVisible() && info != null && info.sublength > 0 ) {
      x        = Math.max( 0, Math.min( (int) info.sublength - 1, (int) (pt.getX() * info.sublength + 0.5) ));
      msgArgs[0]    = new Double( frameBuf[0][x] );
      msgArgs[1]    = new Double( frameBuf[1][x] );
      cursorInfo[0]   = msgCursorX.format( msgArgs );
      cursorInfo[1]   = msgCursorY.format( msgArgs );
      cursorInfo[2]   = msgCursorTime.formatTime( new Double( (pt.getX() * info.span.getLength() +
                                  info.span.getStart()) / rate ));
//System.err.println( "cursorInfo[0] = "+cursorInfo[0]+"; cursorInfo[1] = "+cursorInfo[1] );
      observer.showCursorInfo( cursorInfo );
    }
  }

// ---------------- VectorDisplay.Listener interface ----------------

  /**
   *  Called after the user was drawing
   *  inside the trajectory panels.
   *  This will linearily interpolate the
   *  data to full rate and update the
   *  trajectory files
   *
   *  @synchronization  attemptShared on DOOR_TIME, attemptExclusive on DOOR_TRNSMTE
   */
  public void vectorChanged( VectorDisplay.Event e )
  {
/* EEE   
    AudioTrail            at;
    Span              changedSpan = (Span) e.getActionObject();
    Span              writeSpan;
    int                i, j, k, factor, ch, start, stop, maxSource, len, interpLen;
    float[][]            interpBuf;
    float[]              chBuf;
    float              f1, f2, interpWeight;
    CompoundSessionObjEdit      edit;
    Vector              collTrns;
    BlendContext          bc      = root.getBlending();

    if( trns == null || info == null ) return;

    if( !doc.bird.attemptShared( Session.DOOR_TIME, 200 )) return;
    try {
      if( !doc.bird.attemptExclusive( Session.DOOR_TRNSMTE, 200 )) return;
      collTrns= new Vector( 1 );
      collTrns.add( trns );
      edit  = new CompoundSessionObjEdit( this, doc, collTrns, Transmitter.OWNER_TRAJ,
                                          null, null, Session.DOOR_TIMETRNSMTE );
      try {
        at    = trns.getAudioTrail();
        factor  = info.getDecimationFactor();
        if( factor == 1 ) {   // fullrate buffer can be written directly
          writeSpan   = new Span( changedSpan.getStart() + info.span.getStart(),
                      changedSpan.getStop()  + info.span.getStart() );
          bs      = at.beginOverwrite( writeSpan, bc, edit );
          at.continueWrite( bs, frameBuf, (int) changedSpan.getStart(), (int) changedSpan.getLength() );
          at.finishWrite( bs, edit );
        } else {        // subrate buffers must be upsampled
          start    = (int) changedSpan.getStart();
          stop    = (int) changedSpan.getStop() - 1;
          if( start > stop ) return;
          writeSpan   = new Span( (start * factor) + info.span.getStart(),
                      (stop  * factor) + info.span.getStart() + 1 );
          bs      = at.beginOverwrite( writeSpan, bc, edit );
          maxSource   = 4096 / factor;
          if( maxSource > 0 ) {
            interpBuf   = new float[2][4096];
          } else {
            maxSource   = 1;
            interpBuf   = new float[2][maxSource * factor];
          }
         
          // we precalculate the linear interpolation weights
          // to improve performance
          interpWeight = 1.0f / (float) factor;
          while( start < stop ) {
            len      = Math.min( stop - start, maxSource );
            interpLen   = len * factor;
            for( ch = 0; ch < 2; ch++ ) {
              for( i = start, k = 0; k < interpLen; ) {
                f1    = frameBuf[ch][i++];
                f2    = (frameBuf[ch][i] - f1) * interpWeight;
                chBuf   = interpBuf[ch];
                for( j = 0; j < factor; j++ ) {
                  chBuf[k++] = (float) j * f2 + f1;
                }
              }
            }
            at.continueWrite( bs, interpBuf, 0, interpLen );
            start += len;
          }
         
          at.continueWrite( bs, frameBuf, stop, 1 );  // last sample not interpolated
         
          at.finishWrite( bs, edit );
        } // if( info.idx == 0 )
       
        edit.end();    // fires doc.tc.modified()
        doc.getUndoManager().addEdit( edit );
      }
      catch( IOException e1 ) {
        edit.cancel();
        System.err.println( e1.getLocalizedMessage() );
      }
      finally {
        doc.bird.releaseExclusive( Session.DOOR_TRNSMTE );
      }
    }
    finally {
      doc.bird.releaseShared( Session.DOOR_TIME );
    }
*/
  }

  public void vectorSpaceChanged( VectorDisplay.Event e ) {}

// ---------------- DynamicListening interface ----------------

    public void startListening()
    {
    doc.timeline.addTimelineListener( this );
    doc.getTransmitters().addListener( transmittersListener );
    xEditor.addMouseMotionListener( cursorListener );
    yEditor.addMouseMotionListener( cursorListener );

    loadFrames( false );
//System.err.println( "simpletransmittereditor starts listening" );
    }

    public void stopListening()
    {
    doc.timeline.removeTimelineListener( this );
    doc.getTransmitters().removeListener( transmittersListener );
    xEditor.removeMouseMotionListener( cursorListener );
    yEditor.removeMouseMotionListener( cursorListener );
//System.err.println( "simpletransmittereditor stops listening" );
    }
// ---------------- ToolListener interface ----------------

  /**
   *  Invoked by the <code>TimelineFrame</code>'s
   *  tool bar. Note that the listener is registered by <code>TimelineFrame</code>
   *  automatically if it encounteres a <code>TransmitterEditor</code> implementing this interface!
   *  This method directly calls the <code>toolChanged</code> method of the vector editors.
   */
  public void toolChanged( ToolActionEvent e )
  {
    xEditor.toolChanged( e );
    yEditor.toolChanged( e );
  }
// ---------------- TimelineListener interface ----------------

  public void timelineChanged( TimelineEvent e )
    {
        loadFrames( false );
    // repaint();
    }

  public void timelineScrolled( TimelineEvent e )
    {
        loadFrames( false );   // XXX should check if we need update all parts
    // repaint();
    }

  public void timelineSelected( TimelineEvent e ) {}
  public void timelinePositioned( TimelineEvent e ) {}
}
TOP

Related Classes of de.sciss.meloncillo.transmitter.SimpleTransmitterEditor

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.