/* * Auralizor.java * * Created on April 2, 2004, 3:31 PM */ package org.das2.graph; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.logging.Level; import java.util.logging.Logger; import org.das2.datum.Units; import javax.sound.sampled.*; import javax.swing.JPanel; import org.das2.datum.Datum; import org.das2.datum.LoggerManager; import org.das2.datum.UnitsConverter; import org.das2.qds.DataSetUtil; import org.das2.qds.FlattenWaveformDataSet; import org.das2.qds.QDataSet; import org.das2.qds.SemanticOps; import org.das2.qds.ops.Ops; import org.das2.util.DebugPropertyChangeSupport; /** * Stream QDataSet to the sound system, using DEPEND_0 to control * sampling rate. Note this does not support the new waveform packet * scheme introduced a few years ago, and can easily be made to support * it. * @author Owner */ public final class Auralizor { private static final Logger logger= LoggerManager.getLogger("das2.graph"); private static final int EXTERNAL_BUFFER_SIZE = 100000; ByteBuffer buffer; byte[] buf; SourceDataLine line = null; double min; double max; QDataSet ds; QDataSet dep0; int currentRecord= 0; boolean playing= false; boolean hasListeners= false; PropertyChangeSupport pcs= new DebugPropertyChangeSupport(this); /** * set the dataset to stream. The dataset should be * rank 1 or a rank 2 waveform, and have DEPEND_0 which is convertible * to seconds or be a time location unit. * @param ds the rank 1 dataset with DEPEND_0 convertible to seconds or be a time location unit, or rank 2 waveform. */ public final void setDataSet( QDataSet ds ) { switch (ds.rank()) { case 2: this.ds= new FlattenWaveformDataSet(ds); break; case 1: this.ds= ds; break; default: throw new IllegalArgumentException("dataset must be rank 1 or rank 2 waveform"); } this.dep0= (QDataSet) ds.property(QDataSet.DEPEND_0); min= -1; max= 1; QDataSet yrange= Ops.extent(this.ds); min= yrange.value(0); max= yrange.value(1); if ( min==max ) { max= Math.abs(min); if ( max==min ) max= max * 10; if ( max==0 ) max = max + 10; } } public JPanel getControlPanel() { AuralizorControlPanel acp= new AuralizorControlPanel(); acp.setAuralizor(this); return acp; } public void reset() { currentRecord= 0; playing= false; setPosition(Units.seconds.createDatum(0)); } private Datum position = Units.seconds.createDatum(0); private Datum lastAnnouncedPosition= null; private Datum limit= Units.milliseconds.createDatum(100); public static final String PROP_POSITION = "position"; public Datum getPosition() { return DataSetUtil.asDatum( dep0.slice(currentRecord) ); } public void setPosition(Datum position) { if ( position!=this.position ) { if ( lastAnnouncedPosition==null || lastAnnouncedPosition.subtract(position).abs().gt(limit) ) { this.currentRecord= DataSetUtil.closestIndex( dep0, position ); Datum newPosition= getPosition(); pcs.firePropertyChange( PROP_POSITION, lastAnnouncedPosition, newPosition ); lastAnnouncedPosition= newPosition; } } } private boolean scale = true; public static final String PROP_SCALE = "scale"; public boolean isScale() { return scale; } public void setScale(boolean scale) { boolean oldScale = this.scale; this.scale = scale; pcs.firePropertyChange(PROP_SCALE, oldScale, scale); } public void addPropertyChangeListener(PropertyChangeListener listener) { hasListeners= true; pcs.addPropertyChangeListener(listener); } public void addPropertyChangeListener(String propname,PropertyChangeListener listener) { hasListeners= true; pcs.addPropertyChangeListener(propname,listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } public void removePropertyChangeListener(String propname,PropertyChangeListener listener) { pcs.removePropertyChangeListener(propname,listener); } /** * begin streaming the sound. This will block the current * thread until complete. */ public void playSound() { playing= true; UnitsConverter uc= UnitsConverter.getConverter( SemanticOps.getUnits(dep0).getOffsetUnits(), Units.seconds ); float sampleRate= (float) ( 1. / uc.convert( dep0.value(1)-dep0.value(0) ) ) ; logger.log(Level.FINE, "sampleRate= {0}", sampleRate); AudioFormat audioFormat= new AudioFormat( sampleRate, 16, 1, true, true ); buf= new byte[EXTERNAL_BUFFER_SIZE]; buffer= ByteBuffer.wrap(buf); buffer.order( ByteOrder.BIG_ENDIAN ); DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); try { line = (SourceDataLine) AudioSystem.getLine(info); line.open(audioFormat); line.addLineListener(getLineListener()); } catch (LineUnavailableException e) { throw new RuntimeException(e); } line.start(); int ibuf=0; while ( currentRecord