/* * ------------------------------------------------------------------------- * $Id: Harmonic.java,v 1.4 2006/01/31 20:02:09 estewart Exp $ * ------------------------------------------------------------------------- * Copyright (c) 1999 Visual Numerics Inc. All Rights Reserved. * * This software is confidential information which is proprietary to * and a trade secret of Visual Numerics, Inc. Use, duplication or * disclosure is subject to the terms of an appropriate license * agreement. * * VISUAL NUMERICS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. VISUAL * NUMERICS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR * ITS DERIVATIVES. *-------------------------------------------------------------------------- */ package com.imsl.demo.Harmonic; import javax.swing.*; import javax.swing.event.*; import java.awt.*; import java.awt.event.*; import java.text.DecimalFormat; import com.imsl.math.FFT; import com.imsl.chart.*; import com.imsl.demo.gallery.Describe; /** * Reads from a set of .wav files, plots the waveform, computes * and plots an FFT on request. * * @author R.B.E.Taylor * @created October 23, 2002 */ public class Harmonic extends JFrameChart implements ActionListener, ChangeListener, MouseListener, MouseMotionListener { private Chart chart; private AxisXY axis; private Data data; private JSlider slider; private JTextField displayField; private ButtonGroup selectCelloString; private boolean zoom, iszoom; private double[] from, to, current; private double[] t, y, t_sub, y_sub; private String plotColor, plotLabel; public Harmonic(boolean exitOnClose) { setTitle("Harmonic Analysis"); if (!exitOnClose) { // remove the WindowListener, installed by JFrameChart, that // exits the application when the window is closed. Object l[] = getListeners(java.awt.event.WindowListener.class); for (int k = 0; k < l.length; k++) { removeWindowListener((java.awt.event.WindowListener)l[k]); } } // Set default values. zoom = true; getWavData("Astring.wav"); plotColor = "orange"; plotLabel = "A String"; DecimalFormat tFormat = new DecimalFormat("0.000"); DecimalFormat yFormat = new DecimalFormat("###"); chart = getChart(); axis = new AxisXY(chart); axis.getAxisX().getAxisLabel().setTextFormat(tFormat); axis.getAxisX().getAxisLabel().setTextAngle(90); axis.getAxisX().getAxisTitle().setTitle("Time, sec"); axis.getAxisY().getAxisLabel().setTextFormat(yFormat); axis.getAxisY().getAxisTitle().setTitle("Relative Amplitude"); from = new double[2]; to = new double[2]; current = new double[2]; from[0] = 0.0; from[1] = 128.0; to[0] = 0.8; to[1] = -128; // Add MouseListener and MouseMotionListener to chart panel. chart.getLegend().setPaint(true); chart.getLegend().setViewport(0.65, 0.85, 0.05, 0.15); getPanel().addMouseListener(this); getPanel().addMouseMotionListener(this); Container cp = getContentPane(); // Create components in control panel. ButtonGroup selectCelloString = new ButtonGroup(); JRadioButton Astringbutton = new JRadioButton("A String", true); Astringbutton.addActionListener(this); JRadioButton Dstringbutton = new JRadioButton("D String", false); Dstringbutton.addActionListener(this); JRadioButton Gstringbutton = new JRadioButton("G String", false); Gstringbutton.addActionListener(this); JRadioButton Cstringbutton = new JRadioButton("C String", false); Cstringbutton.addActionListener(this); selectCelloString.add(Astringbutton); selectCelloString.add(Dstringbutton); selectCelloString.add(Gstringbutton); selectCelloString.add(Cstringbutton); JPanel stringPanel = new JPanel(); stringPanel.setLayout(new GridLayout(2,2)); stringPanel.add(Astringbutton); stringPanel.add(Dstringbutton); stringPanel.add(Gstringbutton); stringPanel.add(Cstringbutton); JButton draw = new JButton("Show Full Sample"); draw.addActionListener(this); JButton reset = new JButton("Reset View"); reset.addActionListener(this); JButton transform = new JButton("Calculate FFT"); transform.addActionListener(this); JPanel buttonPanel = new JPanel(); buttonPanel.add(stringPanel); buttonPanel.add(transform); buttonPanel.add(draw); buttonPanel.add(reset); // Create the display panel JPanel displayPanel = new JPanel(); displayField = new JTextField("Current position: t = ?? Y = ??", 53); displayField.setBorder(null); displayField.setEditable(false); displayPanel.add(displayField); cp.add(buttonPanel, BorderLayout.NORTH); cp.add(displayPanel, BorderLayout.SOUTH); drawGraph(); Describe des = new Describe(this, "/com/imsl/demo/Harmonic/Harmonic.html"); des.show(); Dimension ds = des.getSize(); Dimension ss = getToolkit().getScreenSize(); int h = Math.min(ss.width/2, ss.height-ds.height-32); int w = (int)(h/0.8); setSize(w, h); setLocation(ss.width-ds.width, ds.height); setResizable(false); } // Get the description line. private String getDescription() { DecimalFormat nf = new DecimalFormat("0.000"); StringBuffer sb = new StringBuffer(); if (iszoom) { sb.append("Zooming In from: "); sb.append("t = " + nf.format(from[0]) + ", Y = " + nf.format(from[1])); sb.append(" To: "); sb.append("t = " + nf.format(to[0]) + ", Y = " + nf.format(to[1])); } else { sb.append("Cursor At: t = " + nf.format(current[0]) + ", Y = " + nf.format(current[1])); } return sb.toString(); } // Get data from .wav files: private void getWavData(String wavefilename) { int numWavBytes; byte[] byteStream; float bytesPerSec; try { AccessWaveFile fileobject = new AccessWaveFile(wavefilename); numWavBytes = fileobject.getAvailableBytes(); // workaround compression of jar file issue by hardcoding values. switch (wavefilename.charAt(0)) { case 'A': numWavBytes = 86272; break; case 'D': numWavBytes = 75904; break; case 'G': numWavBytes = 81664; break; case 'C': numWavBytes = 92672; break; default: numWavBytes = fileobject.getAvailableBytes(); break; } byteStream = fileobject.getByteStream(numWavBytes); bytesPerSec = fileobject.getBytesPerSec(); } catch (Exception e) { JOptionPane.showMessageDialog(this, e.getMessage(), "Exception", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); System.exit(1); return; } // Convert data and construct t-values: t = new double[numWavBytes]; y = new double[numWavBytes]; for (int i = 0; i < y.length; i++) { y[i] = (double) byteStream[i]; t[i] = (double) i/bytesPerSec; //time in seconds } } // Draw the graph. private void drawGraph() { // Subset it before plotting: if (zoom) { int i = 0; double min = Math.min(from[0], to[0]); while (t[i] < min) i++; int i_init = i; double max = Math.max(from[0], to[0]); while (t[i] <= max) { i++; if (i == t.length-1) break; } int i_final = i; t_sub = new double[i_final - i_init + 1]; y_sub = new double[i_final - i_init + 1]; for (i = 0; i <= i_final - i_init; i++) { t_sub[i] = t[i + i_init]; y_sub[i] = y[i + i_init]; } } else { t_sub = new double[t.length]; y_sub = new double[t.length]; for (int i = 0; i < y_sub.length; i++) { t_sub[i] = t[i]; y_sub[i] = y[i]; } } // If in zooming mode, then zoom the graph. Otherwise, use default ranges. if (zoom) { axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF); axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF); axis.getAxisX().setWindow(Math.min(from[0], to[0]), Math.max(from[0], to[0])); axis.getAxisY().setWindow(-128.0, 128.0); } else { axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF); axis.getAxisX().setWindow(0.0, t[t.length-1]); axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF); axis.getAxisY().setWindow(-128.0, 128.0); } data = new Data(axis, t_sub, y_sub); data.setTitle(plotLabel); data.setDataType(Data.DATA_TYPE_LINE); data.setLineColor(plotColor); } private void plotNewFFT() { // Calculate the FFT : double[] t_lim = new double[2]; t_lim[0] = t_sub[0]; t_lim[1] = t_sub[t_sub.length - 1]; double[] a; FFT transform = new FFT(y_sub.length); a = transform.forward(y_sub); // Calculate the power spectrum, as per documentation, // including a factor normalising to the length of the input: double[] power = new double[y_sub.length/2]; //should take care of n odd/even double[] f = new double[power.length]; f[0] = (double) 0.0; power[0] = Math.abs(a[0])/y_sub.length; for (int i = 1; i < power.length; i++) { f[i] = i/(t_lim[1] - t_lim[0]); power[i] = Math.sqrt(a[2*i-1]*a[2*i-1] + a[2*i]*a[2*i])/y_sub.length; } PlotFFT pf = new PlotFFT(this); pf.draw(f, power, t_lim, plotLabel, plotColor); pf.show(); } // Implement ActionListener public void actionPerformed(ActionEvent e) { data.remove(); // clear current trace if (e.getActionCommand().equals("Show Full Sample")) { zoom = false; } else if (e.getActionCommand().equals("Reset View")) { zoom = true; from[0] = 0.0; from[1] = 128.0; to[0] = 0.8; to[1] = -128; } else if (e.getActionCommand().equals("A String")) { plotColor = "orange"; plotLabel = "A String"; getWavData("Astring.wav"); } else if (e.getActionCommand().equals("C String")) { plotColor = "red"; plotLabel = "C String"; getWavData("Cstring.wav"); } else if (e.getActionCommand().equals("D String")) { plotColor = "magenta"; plotLabel = "D String"; getWavData("Dstring.wav"); } else if (e.getActionCommand().equals("G String")) { plotColor = "blue"; plotLabel = "G String"; getWavData("Gstring.wav"); } else if (e.getActionCommand().equals("Calculate FFT")) { plotNewFFT(); } drawGraph(); repaint(); } // Implement ChangeListener public void stateChanged(ChangeEvent e) { } // Implement MouseListener public void mouseClicked(MouseEvent e) { } public void mousePressed(MouseEvent e) { // Get the starting position for zooming. axis.mapDeviceToUser(e.getX(), e.getY(), from); axis.mapDeviceToUser(e.getX(), e.getY(), to); zoom = true; iszoom = true; } public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY(); // The selection must be from 2 different points. int[] point = new int[2]; axis.mapUserToDevice(from[0], from[1], point); if (point[0] == x) return; if (point[1] == y) return; // Get the ending position for zooming. axis.mapDeviceToUser(x, y, to); iszoom = false; data.remove(); drawGraph(); repaint(); } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } // Implement MouseMotionListener public void mouseDragged(MouseEvent e) { // Draw the selection square and update display information. paint(e, true); displayField.setText(getDescription()); } public void mouseMoved(MouseEvent e) { // Update display information. axis.mapDeviceToUser(e.getX(), e.getY(), current); displayField.setText(getDescription()); } // Draw the selection in different color. protected void paint(MouseEvent mouseEvent, boolean erase) { Graphics2D g = (Graphics2D) Harmonic.this.getPanel().getGraphics(); if (erase) paint(g); axis.mapDeviceToUser(mouseEvent.getX(), mouseEvent.getY(), to); paint(g); g.dispose(); } protected void paint(Graphics2D g) { Color colorFill = new Color(255,0,255,128); Color colorOutline = new Color(0,255,0); int[] pointA = new int[2]; int[] pointB = new int[2]; axis.mapUserToDevice(from[0], from[1], pointA); axis.mapUserToDevice(to[0], to[1], pointB); Shape shape = new Rectangle(Math.min(pointA[0], pointB[0]), Math.min(pointA[1], pointB[1]), Math.abs(pointA[0]-pointB[0]), Math.abs(pointA[1]-pointB[1])); g.setXORMode(colorOutline); g.draw(shape); g.setXORMode(colorFill); g.fill(shape); } public static void main(String args[]) { boolean exitOnClose = true; if (args.length > 0 && args[0].equals("-noexit")) exitOnClose = false; new Harmonic(exitOnClose).show(); } }