6.5 El programa RTASA

El programa usado en esta práctica para calcular el espectro de una señal de audio se llama RTASA (Real Time Audio Spectrum Analyzer).

6.5.1 Entradas

RTASA acepta una secuencia RAW (sin cabecera) de muestras a través de entrada estándar. Debe tratarse de una secuencia de muestras de 16 bits, con signo, en formato little endian (el usado en las máquinas Intel). Se esperan dos canales.

6.5.2 Controles

RTASA acepta parámetros iniciales desde la línea de comandos e interactivamente (durante la ejecución), a través de diferentes elementos de entrada.

Los parámetros iniciales son:

  1. El número de bandas de frecuencia analizadas. Por requerimientos de la FFT debe ser una potencia de 2.
  2. La frecuencia de muestreo (en muestras por segundo). Dicha frecuencia debería coincidir con la frecuencia de muestreo de la secuencia de entrada.

Los otros controles interactivos son:

  1. Y-Scale Control: Controla la escala del eje Y del espectro mostrado en la ventana Spectrum. El control se realiza con la rueda del ratón. El paso del incremento o del decremento se controla con los botones.
  2. Elasticity Control: Controla la elasticidad de la gráfica que muestra el hitorial reciente del espectro mostrado en la ventana Spectrum. Dicha elasticidad se puede indicar con el ratón, arrastrando el slider o introduciéndo en el campo de texto.

6.5.3 Salidas

La única salida del programa es la gráfica del espectro (ventana Spectrum). En la parte superior aparece el espectro del canal izquierdo y abajo, el del canal derecho. La ventana es redimensionable.

6.5.4 RTASA.java

/* Real Time Audio Spectrum Analyzer. */  
 
/*  
 * Versión inicial creada por Vicente González Ruiz <vruiz@ual.es>.  
 *  
 * Mejoras introducidas por Manuel Marín <niram@eresmas.net>  
 *   referentes al cálculo de la frecuencia con el puntero del ratón.  
 *  
 * gse. 2006.  
 */  
 
import java.awt.*;  
import javax.swing.*;  
import java.awt.event.*;  
 
//import java.net.DatagramSocket;  
//import java.net.InetAddress;  
//import java.net.DatagramPacket;  
import java.io.IOException;  
 
public class RTASA  
    extends JComponent  
    implements Runnable,  
               ComponentListener,MouseMotionListener {  
 
//////////////////////////////////////////////////////  
/* Variables y metodos relacionados con el raton */  
 
  //Variables para almacenar la posición actual del raton//  
  Dimension d;  
  int posX = 0, posY=0;  
  int visualiza_frec;  
 
  //Metodo de inicializacion de la funcion de rastreo del raton//  
  public void init() {  
    d = this.getSize();  
    addMouseMotionListener(this);  
  }  
 
  //Metodo de realizacion de acciones en el caso de arrastrar y soltar//  
  public void mouseDragged(MouseEvent me) {}  
 
  //Metodo de realizacion de acciones en el caso de movimiento del raton dentro de la pantalla  
  //grafica. Dichas acciones consisten en calcular el valor de la frecuencia sobre el que  
  //esta situado el puntero del raton. Para realizar el calculo, se multiplica la variable  
  //bandWidth por la coordenada X de la posicion actual del raton en ese momento, restandole  
  //a dicho resultado el desplazamiento de 20 pixeles existente, ya que la abcisa empieza en el  
  //pixel 20. Para el calculo del valor de este desplazamiento se multiplica la variable  
  //bandWidth por 20. El valor final de toda esta operacion lo recoge la variable visualiza_frec  
  public void mouseMoved(MouseEvent me) {  
    if (me.getX()>=20){ //Si el raton está situado a la derecha de los 20 pixeles de xoffset//  
       visualiza_frec = (int)(bandWidth*me.getX()-(bandWidth*20)); //Obtiene la frecuencia con la coordenada x//  
       //Escribe en la consola de texto el valor en Hz. Se puede prescindir de esta linea  
       //ya que dicho valor ya aparece en la pantalla grafica  
       System.out.println("Frecuencia: " + visualiza_frec +" Hz");  
    }else{ //Si se sale del margen izquierdo, para evitar escribir valores negativos//  
       visualiza_frec = 0;  
       System.out.println("Frecuencia: " + visualiza_frec +" Hz");  
    }  
 
    //Se almacenan las coordenadas actuales x,y del raton//  
    posX = me.getX();  
    posY = me.getY();  
 
    //Actualizar la pantalla grafica//  
    this.repaint();  
  }  
 
/* Fin de las variables y metodos relacionados con el raton */  
//////////////////////////////////////  
 
    /* Número de bandas (por defecto). */  
    static int NUMBER_OF_BANDS = 512;  
 
    /* Número de muestras por segundo (por defecto). */  
    static int SAMPLE_RATE = 44100;  
 
    /* Altura inicial de la ventana. */  
    static int WINDOW_HEIGHT = 512;  
 
    /* Dimensiones de la ventana. */  
    int windowHeight, windowWidth;  
 
    JFrame frame;  
    Thread thread; /* Este hilo */  
 
    /* Número de bytes en cada muestra de audio. */  
    int bytesPerSample = 2;  
 
    /* Número de canales. */  
    int channels = 2;  
 
    /* Número de muestras por segundo (frecuencia de muestreo) */  
    float sampleRate;  
    /* Ancho de banda mostrado */  
    float totalBandWidth;  
    /* Ancho de banda de cada banda */  
    float bandWidth;  
 
    YScale yScale;  
    ElasticityControl ec;  
    double elasticity;  
 
    /* Factor de escala en el eje Y */  
    //float scale;  
 
    /* Buffer de audio. */  
    byte[] audioBuffer;  
 
    /* Tamaño del buffer de audio en bytes. */  
    int audioBufferSize;  
 
    double[] leftSpectrum;  
    double[] rightSpectrum;  
    double[][] leftBuffer;  
    double[][] rightBuffer;  
    double[] window;  
 
    /* Arena encima del espectro */  
    int[] leftSand, rightSand;  
    /* Gravedad aplicada a la arena (0 -> ausencia de gravedad, 1 -> gravedad infinita */  
    double sandGravity=0.1;  
    /* Tamaño del grano de arena */  
    int sandSize = 10;  
 
    int numberOfBands;  
    int numberOfSamples;  
 
    //int numberOfSpectrumsPerPacket;  
 
    //static final int PACKET_SIZE = 1024;  
    //static final int SIZEOF_SAMPLE = 2;  
    //static final int BUF_SIZE = 4096;  
 
    //static final int PORT = 6789;  
    //DatagramSocket socket;  
    //DatagramPacket sendPacket;  
    //DatagramPacket receivePacket;  
    Color rojoOscuro;  
 
    public RTASA(int numberOfBands, float sampleRate) throws IOException {  
        System.out.println("+-----------------------------------+");  
        System.out.println("| Real Time Audio Spectrum Analizer |");  
        System.out.println("+-----------------------------------+");  
        this.sampleRate = sampleRate;  
        System.out.println("Sample Rate = " + sampleRate + " samples/second");  
        this.numberOfBands = numberOfBands;  
        int tmp = numberOfBands;  
        boolean numberOfBandsIsAPowerOfTwo = true;  
        int i = 0;  
        while(tmp>1) {  
            if((tmp%2)==1) {  
                numberOfBandsIsAPowerOfTwo = false;  
                break;  
            }  
            tmp >>= 1;  
            i++;  
        }  
        if(numberOfBandsIsAPowerOfTwo==false) {  
            System.out.println("I need a number of bands power of two ...");  
            numberOfSamples = (numberOfSamples>>i);  
        }  
        System.out.println("Number of bands = " + numberOfBands);  
        numberOfSamples = numberOfBands*2;  
        System.out.println("Number of samples = " + numberOfSamples);  
 
        audioBufferSize = channels * numberOfSamples * bytesPerSample;  
        audioBuffer = new byte[audioBufferSize];  
 
        //numberOfSpectrumsPerPacket = BUF_SIZE/numberOfSamples/2;  
        leftBuffer = new double[numberOfSamples][2];  
        rightBuffer = new double[numberOfSamples][2];  
        leftSpectrum = new double[numberOfSamples];  
        rightSpectrum = new double[numberOfSamples];  
        window = new double[numberOfSamples];  
        leftSand = new int[numberOfSamples];  
        rightSand = new int[numberOfSamples];  
        computeWindow(1);  
        totalBandWidth = computeTotalBandWidth(sampleRate);  
        System.out.println("Total Band Width = " + totalBandWidth + " Hertzs");  
        bandWidth = computeBandWidth(sampleRate, numberOfBands);  
        System.out.println("Band Width = " + bandWidth + " Hertzs");  
        //setDoubleBuffered(true);  
 
        // Create and set up the RTASA window  
        frame = new JFrame("RTASA - Spectrum");  
 
        //scale = numberOfSamples;  
        // Controlador de la scala en el eje Y  
        yScale = new YScale(numberOfSamples, numberOfSamples/10);  
 
        frame.getContentPane().add(this);  
        windowWidth = numberOfBands + 10;  
        windowHeight = WINDOW_HEIGHT;  
        frame.setSize(windowWidth, windowHeight);  
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
        frame.addComponentListener(this);  
        Container content = frame.getContentPane();  
        content.setBackground(Color.black);  
        frame.show();  
 
        // Controlador de la elasticidad del espectro azul  
        ec = new  ElasticityControl();  
 
        rojoOscuro = new Color(100, 0, 0);  
 
        //Capturar movimiento del raton//  
        init();  
    }  
 
    /* Lanza el hilo */  
    public void start() {  
        thread = new Thread(this);  
        thread.setName("RealTimeAudioSpectrumAnalizer");  
        thread.start();  
    }  
 
    /* Detiene el hilo */  
    public void stop() {  
        thread = null;  
    }  
 
    public void computeWindow(int win_type) {  
        Window.computeCoefficients(win_type, window);  
    }  
 
    public float computeTotalBandWidth(float sampleRate) {  
        return sampleRate/2;  
    }  
 
    public float computeBandWidth(float sampleRate, int numberOfBands) {  
        return (sampleRate/2)/numberOfBands;  
    }  
 
    public void setGravity(float gravity) {  
        this.sandGravity = gravity;  
    }  
 
    public void run() {  
        try {  
            for(;;) {  
                //int bytes_read = System.in.read(audioBuffer);  
                //System.out.print("S");  
                //socket.send(sendPacket);  
                //System.out.print("R");  
                //for(int i=0; i<numberOfSamples*SIZEOF_SAMPLE/PACKET_SIZE; i++) {  
                //socket.receive(receivePacket);  
                //for(int j=0; j<numberOfSpectrumsPerPacket; j++) {  
                int total = 0;  
                while(total<audioBufferSize) {  
                    total += System.in.read(audioBuffer,total,audioBufferSize-total);  
                }  
                //System.out.println(total + " ");  
                for(int i=0; i<numberOfSamples; i++) {  
                    //System.out.print(".");  
                    /* Parte real, muestra izquierda. */  
                    leftBuffer[i][0]  
                        = (double)(audioBuffer[4*i+1]*256 +  
                                   audioBuffer[4*i]);  
                    /* Parte imaginaria, muestra izquierda. */  
                    leftBuffer[i][1] = 0.0;  
                    /* Parte real, muestra derecha. */  
                    rightBuffer[i][0]  
                        = (double)(audioBuffer[4*i+3]*256 +  
                                   audioBuffer[4*i+2]);  
                    /* Parte imaginaria, muestra derecha. */  
                    rightBuffer[i][1] = 0;  
                }  
 
                /* Transformada de Fourier del canal izquierdo. */  
                FFT.direct(leftBuffer);  
                /* Transformada de Fourier del canal derecho. */  
                FFT.direct(rightBuffer);  
 
                /* Obtenemos la elasticitad de la arena. */  
                elasticity = ec.getElasticity();  
 
                /* Calculamos el espectro (módulo). */  
                for(int i=0; i<numberOfSamples; i++) {  
                    leftSpectrum[i]  
                        = Math.sqrt(leftBuffer[i][0]*leftBuffer[i][0] +  
                                    leftBuffer[i][1]*leftBuffer[i][1]);  
                    rightSpectrum[i]  
                        = Math.sqrt(rightBuffer[i][0]*rightBuffer[i][0] +  
                                    rightBuffer[i][1]*rightBuffer[i][1]);  
                }  
 
                /* Calculamos la arena. */  
                for(int i=0; i<numberOfSamples; i++) {  
                    leftSand[i]  
                        = (int)((1-elasticity)*leftSand[i] +  
                                elasticity*leftSpectrum[i]);  
                    rightSand[i]  
                        = (int)((1-elasticity)*rightSand[i] +  
                                elasticity*rightSpectrum[i]);  
                }  
 
                /* Pintamos. */  
                repaint();  
            }  
 
        } catch (IOException e) {  
            System.out.println("Error in the pipe.");  
        }  
    }  
 
    // Este método no debería estar aquí. Si creamos una clase para  
    // controlar el tamaño de la ventana debería de llamarse desde  
    // allí. Lo mismo debería de ocurrir si creamos una clase para  
    // controlar la frecuencia de muestreo.  Esto se debe de hacer así  
    // porque sólo cuando estas clases están trabajando es cuando debe  
    // de cambiar la escala y no debería de pintarse siempre que se  
    // presenta el espectro.  
    void drawHz(Graphics g) {  
        Color color = Color.black;  
        g.setColor(color);  
        for(int i=10; i<numberOfSamples/2; i+=50) {  
            g.drawString(i*bandWidth + "", i, 10);  
        }  
    }  
 
    /* Pinta la ventana */  
    public void paintComponent(Graphics g) {  
        int xOffset = 20;  
        int yOffset = 25;  
        Color color = Color.red;  
        //g.setColor(color);  
 
        /* Pintamos el espectro del canal izquiero arriba. */  
        //color = new Color(255,0,0);  
        g.setXORMode(new Color(255,0,0));  
        for(int i=0; i<numberOfSamples/2/*256*/; i++) {  
            //Double y = new Double(spectrum[i]/numberOfSamples);  
            //int x = y.intValue();  
            int x = (int)(leftSpectrum[i]/yScale.getScale());  
            //System.out.print(spectrum[i] + " " + x + " ");  
            g.drawLine(i+xOffset, /*460*/yOffset, i+xOffset, /*460-x*/x+yOffset);  
            //paintLine(i,yOffset,i,x+yOffset,g);  
            //int val_i = (data[2*i]*256+data[2*i+1])/256;  
            //int val_i1 = (data[2*(i+1)]*256+data[2*(i+1)+1])/256;  
            //g.drawLine(i,val_i+128,i+1,val_i1+128);  
        }  
 
        /* Pintamos el espectro del canal derecho abajo. */  
        g.setXORMode(/*Color.green*/new Color(0,255,0));  
        for(int i=0; i<numberOfSamples/2/*256*/; i++) {  
            //Double y = new Double(spectrum[i]/numberOfSamples);  
            //int x = y.intValue();  
            int x = (int)(rightSpectrum[i]/yScale.getScale());  
            //System.out.print(rightSpectrum[i] + " " + x + " ");  
            g.drawLine(i+xOffset, /*460*/yOffset+windowHeight-85, i+xOffset, /*460-x*/windowHeight-85+yOffset-x);  
            //paintLine(i,yOffset,i,x+yOffset,g);  
            //int val_i = (data[2*i]*256+data[2*i+1])/256;  
            //int val_i1 = (data[2*(i+1)]*256+data[2*(i+1)+1])/256;  
            //g.drawLine(i,val_i+128,i+1,val_i1+128);  
        }  
 
        //color = Color.blue;  
        //g.setColor(color);  
        g.setXORMode(/*Color.blue*/new Color(0,0,255));  
        for(int i=0; i<numberOfSamples/2; i++) {  
            int x = (int)(leftSand[i]/yScale.getScale());  
            g.drawLine(i+xOffset, /*460-x*/x+yOffset, i+xOffset, /*450-x*/x+sandSize+yOffset);  
        }  
        //color = Color.green;  
        //g.setColor(color);  
 
        //color = Color.cyan;  
        //g.setColor(color);  
        //g.setXORMode(Color.cyan);  
        //g.setColor(Color.cyan);  
        //color = new Color(10,10,200);  
        //g.setXORMode(color);  
        for(int i=0; i<numberOfSamples/2; i++) {  
            int x = (int)(rightSand[i]/yScale.getScale());  
            g.drawLine(i+xOffset, /*460-x*/yOffset+windowHeight-85-x, i+xOffset, /*450-x*/yOffset+windowHeight-85-sandSize-x);  
        }  
        //if(dakl) drawHz(g);  
        g.setXORMode(Color.white);  
        for(int i=0; i<numberOfBands; i+= 50) {  
            g.drawString("" + (int)(i*bandWidth), i+xOffset, 15);  
            g.drawString("" + (int)(i*bandWidth), i+xOffset, /*505*/windowHeight-40);  
        }  
 
        //g.setColor(Color.red);  
        for(int i=0; i<numberOfBands; i+= 50) {  
            g.drawLine(i+xOffset,18,i+xOffset,21);  
            g.drawLine(i+xOffset,windowHeight-84+yOffset+3,i+xOffset,windowHeight-84+yOffset+6);  
        }  
 
        //Mostrar la frecuencia siguiendo al puntero del raton//  
        if (posX>=xOffset && posX<=windowWidth){ //para no salirse de los márgenes establecidos//  
          g.drawString("" +visualiza_frec + " Hz", posX, posY);  
          //Mostrar linea vertical cuyas coordenadas de inicio y fin son los siguientes dos puntos:  
          //  origen(posX, 0)  
          //  fin(posX, windowHeight)  
          g.drawLine(posX,0,posX,windowHeight);  
        }  
    }  
 
    public void componentHidden(ComponentEvent e) {  
    }  
 
    public void componentMoved(ComponentEvent e) {  
    }  
 
    public void componentResized(ComponentEvent e) {  
        Component c = e.getComponent();  
        windowHeight = c.getSize().height;  
        //System.out.println(c.getSize().width + " " + c.getSize().height);  
    }  
 
    public void componentShown(ComponentEvent e) {  
    }  
 
    public static void main(String[] args) throws Exception {  
        int numberOfBands = NUMBER_OF_BANDS;  
        float sampleRate = SAMPLE_RATE;  
        if(args.length>=1) {  
            try {  
                numberOfBands = new Integer(args[0]).intValue();  
            }  catch (NumberFormatException e) {  
                System.out.println("Error parsing \"" + args[1] + "\"");  
            }  
        }  
        if(args.length>=2) {  
            try {  
                sampleRate = new Float(args[1]).floatValue();  
            }  catch (NumberFormatException e) {  
                System.out.println("Error parsing \"" + args[2] + "\"");  
            }  
        }  
        RTASA analizador = new RTASA(numberOfBands, sampleRate);  
        analizador.start();  
    }  
}  

6.5.5 ElasticityControl.java

import java.awt.*;  
import java.awt.event.*;  
import javax.swing.*;  
import javax.swing.event.*;  
import javax.swing.text.NumberFormatter;  
import java.beans.*;  
import java.util.*;  
 
/**  
 * Esta clase maneja la elasticidad del espectro azul.  
 *  
 * @author Vicente González Ruiz  
 * @version 1-Junio-2004  
 */  
 
/*  
 * SliderDemo3.java is a 1.4 application that requires all the  
 * files in the images/doggy directory.  It adds a formatted text  
 * field to SliderDemo.java.  
 */  
public class ElasticityControl extends JPanel implements /*ActionListener,*/ WindowListener, ChangeListener, PropertyChangeListener {  
 
    static final int ELASTICITY_MIN = 0;  
    static final int ELASTICITY_MAX = 1000;  
    static final int ELASTICITY_INIT = 50;    // Initial elasticity  
    double elasticity = (double)ELASTICITY_INIT/(double)ELASTICITY_MAX;  
    JFormattedTextField textField;  
    JSlider slider;  
 
    public ElasticityControl() {  
        // Colocamos el textField y el slider uno encima de otro  
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));  
 
        // Creamos el label del slider  
        JLabel sliderLabel = new JLabel("Elasticity (x1000): ", JLabel.CENTER);  
        sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);  
 
        // Create the formatted text field and its formatter.  
        java.text.NumberFormat numberFormat = java.text.NumberFormat.getIntegerInstance();  
        NumberFormatter formatter = new NumberFormatter(numberFormat);  
        formatter.setMinimum(new Double((double)ELASTICITY_MIN/(double)ELASTICITY_MAX));  
        formatter.setMaximum(new Double((double)ELASTICITY_MAX));  
        textField = new JFormattedTextField(formatter);  
        textField.setValue(new Integer(ELASTICITY_INIT));  
        textField.setColumns(3); //get some space  
        textField.addPropertyChangeListener(this);  
 
        //React when the user presses Enter.  
        textField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),"check");  
        textField.getActionMap().put("check", new AbstractAction() {  
            public void actionPerformed(ActionEvent e) {  
                if (!textField.isEditValid()) { //The text is invalid.  
                    Toolkit.getDefaultToolkit().beep();  
                    textField.selectAll();  
                } else try {                    //The text is valid,  
                    textField.commitEdit();     //so use it.  
                } catch (java.text.ParseException exc) { }  
            }  
        });  
 
        //Create the slider.  
        slider = new JSlider(JSlider.HORIZONTAL, ELASTICITY_MIN, ELASTICITY_MAX, ELASTICITY_INIT);  
        slider.addChangeListener(this);  
 
        //Turn on labels at major tick marks.  
        slider.setMajorTickSpacing(500);  
        slider.setMinorTickSpacing(50);  
        slider.setPaintTicks(true);  
        slider.setPaintLabels(true);  
        slider.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));  
 
        // Le redefinimos las etiquetas al slider  
        Dictionary labelTable = slider.getLabelTable();  
        JLabel cero = new JLabel("0.0");  
        JLabel puntocinco = new JLabel("0.5");  
        JLabel uno = new JLabel("1.0");  
        labelTable.put(new Integer(ELASTICITY_MIN), cero);  
        labelTable.put(new Integer((ELASTICITY_MAX - ELASTICITY_MIN)/2), puntocinco);  
        labelTable.put(new Integer(ELASTICITY_MAX), uno);  
        slider.setLabelTable(labelTable);  
 
        // Create a subpanel for the label and text field.  
        JPanel labelAndTextField = new JPanel(); //use FlowLayout  
        labelAndTextField.add(sliderLabel);  
        labelAndTextField.add(textField);  
 
        // Put everything together.  
        add(labelAndTextField);  
        add(slider);  
        setBorder(BorderFactory.createEmptyBorder(10,10,10,10));  
        //createAndShowGUI();  
 
        //Make sure we have nice window decorations.  
        JFrame.setDefaultLookAndFeelDecorated(true);  
 
        //Create and set up the window.  
        JFrame frame = new JFrame("RTASA - Elasticity Control");  
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
 
        //Create and set up the content pane.  
        //ElasticityControl animator = new ElasticityControl();  
        this.setOpaque(true); //content panes must be opaque  
        frame.setContentPane(this);  
 
        //Display the window.  
        frame.pack();  
        frame.setVisible(true);  
    }  
 
    double getElasticity() {  
        return elasticity;  
    }  
 
    /** Add a listener for window events. */  
    void addWindowListener(Window w) {}  
 
    //React to window events.  
    public void windowIconified(WindowEvent e) {}  
    public void windowDeiconified(WindowEvent e) {}  
    public void windowOpened(WindowEvent e) {}  
    public void windowClosing(WindowEvent e) {}  
    public void windowClosed(WindowEvent e) {}  
    public void windowActivated(WindowEvent e) {}  
    public void windowDeactivated(WindowEvent e) {}  
 
    /** Listen to the slider. */  
    public void stateChanged(ChangeEvent e) {  
        JSlider source = (JSlider)e.getSource();  
        int elast = (int)source.getValue();  
        elasticity = (double)elast/(double)ELASTICITY_MAX;  
        if (!source.getValueIsAdjusting()) { //done adjusting  
            textField.setValue(new Integer(elast));  
        } else { //value is adjusting; just set the text  
            textField.setText(String.valueOf(elast));  
        }  
    }  
 
    /**  
     * Listen to the text field.  This method detects when the  
     * value of the text field (not necessarily the same  
     * number as you’d get from getText) changes.  
     */  
    public void propertyChange(PropertyChangeEvent e) {  
        if ("value".equals(e.getPropertyName())) {  
            Number value = (Number)e.getNewValue();  
            if (slider != null && value != null) {  
                slider.setValue(value.intValue());  
            }  
        }  
    }  
}  
 

6.5.6 FFT.java

 
/**  
 * This class implements the FFT (Fast Fourier Transform) routines.  
 *  
 * @author Vicente González Ruiz.  
 */  
public class FFT {  
    /**  
     * Constructor for objects of class FFT_1D  
     */  
    public FFT() {  
    }  
    /**  
     * The Fast Fourier Transform (FFT).  
     * The array length must be a power of two.  
     * The array size is [L][2], where each sample is complex.  
     * array[n][0] is the real part and array[n][1] is the imaginary part of  
     * the sample n.  
     *  
     * @author URL: http://www.nauticom.net/www/jdtaft/JavaFFT.htm  
     */  
    static void direct(double[][] array) {  
        double u_r,u_i, w_r,w_i, t_r,t_i; int ln, nv2, k, l, le, le1, j, ip, i, n;  
 
        n = array.length;  
        ln = (int)( Math.log( (double)n )/Math.log(2) + 0.5 );  
        nv2 = n / 2;  
        j = 1;  
        for (i = 1; i < n; i++ ) {  
            if (i < j) {  
                t_r = array[i - 1][0];  
                t_i = array[i - 1][1];  
                array[i - 1][0] = array[j - 1][0];  
                array[i - 1][1] = array[j - 1][1];  
                array[j - 1][0] = t_r;  
                array[j - 1][1] = t_i;  
            }  
            k = nv2;  
            while (k < j) {  
                j = j - k;  
                k = k / 2;  
            }  
            j = j + k;  
        }  
 
        for (l = 1; l <= ln; l++) /* loops thru stages */ {  
            le = (int)(Math.exp( (double)l * Math.log(2) ) + 0.5 );  
            le1 = le / 2;  
            u_r = 1.0;  
            u_i = 0.0;  
            w_r =  Math.cos( Math.PI / (double)le1 );  
            w_i = -Math.sin( Math.PI / (double)le1 );  
            for (j = 1; j <= le1; j++) /* loops thru 1/2 twiddle values per stage */ {  
                for (i = j; i <= n; i += le) /* loops thru points per 1/2 twiddle */ {  
                    ip = i + le1;  
                    t_r = array[ip - 1][0] * u_r - u_i * array[ip - 1][1];  
                    t_i = array[ip - 1][1] * u_r + u_i * array[ip - 1][0];  
 
                    array[ip - 1][0] = array[i - 1][0] - t_r;  
                    array[ip - 1][1] = array[i - 1][1] - t_i;  
 
                    array[i - 1][0] =  array[i - 1][0] + t_r;  
                    array[i - 1][1] =  array[i - 1][1] + t_i;  
                }  
                t_r = u_r * w_r - w_i * u_i;  
                u_i = w_r * u_i + w_i * u_r;  
                u_r = t_r;  
            }  
        }  
    }  
 
    double[][] inverse(  double[][] array ) {  
        double  u_r,u_i, w_r,w_i, t_r,t_i;  
        int     ln, nv2, k, l, le, le1, j, ip, i, n;  
 
        n = array.length;  
            ln = (int)( Math.log( (double)n )/Math.log(2) + 0.5 );  
            nv2 = n / 2;  
            j = 1;  
         for (i = 1; i < n; i++ ) {  
            if (i < j) {  
                t_r = array[i - 1][0];  
                t_i = array[i - 1][1];  
                array[i - 1][0] = array[j - 1][0];  
                array[i - 1][1] = array[j - 1][1];  
                array[j - 1][0] = t_r;  
                array[j - 1][1] = t_i;  
            }  
            k = nv2;  
            while (k < j) {  
                j = j - k;  
                k = k / 2;  
            }  
            j = j + k;  
            }  
 
         for (l = 1; l <= ln; l++) /* loops thru stages */ {  
            le = (int)(Math.exp( (double)l * Math.log(2) ) + 0.5 );  
            le1 = le / 2;  
            u_r = 1.0;  
            u_i = 0.0;  
            w_r =  Math.cos( Math.PI / (double)le1 );  
            w_i =  Math.sin( Math.PI / (double)le1 );  
            for (j = 1; j <= le1; j++) /* loops thru 1/2 twiddle values per stage */ {  
                for (i = j; i <= n; i += le) /* loops thru points per 1/2 twiddle */ {  
                    ip = i + le1;  
                    t_r = array[ip - 1][0] * u_r - u_i * array[ip - 1][1];  
                    t_i = array[ip - 1][1] * u_r + u_i * array[ip - 1][0];  
 
                    array[ip - 1][0] = array[i - 1][0] - t_r;  
                    array[ip - 1][1] = array[i - 1][1] - t_i;  
 
                    array[i - 1][0] =  array[i - 1][0] + t_r;  
                    array[i - 1][1] =  array[i - 1][1] + t_i;  
                }  
                t_r = u_r * w_r - w_i * u_i;  
                u_i = w_r * u_i + w_i * u_r;  
                u_r = t_r;  
            }  
            }  
        return array;  
    } /* end of ifft_1d */  
}

6.5.7 Window.java

 
/**  
 * Write a description of class Window here.  
 *  
 * @author (your name)  
 * @version (a version number or a date)  
 */  
public class Window {  
    /**  
     * Constructor for objects of class Window  
     */  
    public Window() {  
    }  
 
    /**  
     * http://www.nauticom.net/www/jdtaft/JavaWindows.htm  
     */  
    static double[] computeCoefficients( int win_type, double[] coeffs ) {  
        int n;  
        int m;  
        double twopi;  
 
        m = coeffs.length;  
        twopi = 2.*Math.PI;  
        switch( win_type ) {  
            case 1:  /* Hamming   */  
            for( n = 0; n < m; n++ ) {  
                coeffs[n] = 0.54 - 0.46*Math.cos( twopi*n/(m-1) );  
                coeffs[n] *= 0.5*(1. - Math.cos(twopi*n/(m-1)) );  
            }  
            break;  
            case 2:  /* von Hann (sometimes improperly called Hanning)  */  
            for( n = 0; n < m; n++ ) {  
                coeffs[n] = 0.5*(1.0 - Math.cos(twopi*n/(m-1)) );  
            }  
            break;  
            case 3:  /* Blackman  */  
            for( n = 0; n < m; n++ ) {  
                coeffs[n] = 0.42 - 0.5*Math.cos(twopi*n/(m-1)) +  
                           0.08*Math.cos(2.*twopi*n/(m-1));  
            }  
            case 4:  /* Bartlett  */  
            for( n = 0; n <= (m-1)/2; n++ ) {  
                coeffs[n] = 2.*n/(m-1);  
            }  
            for( n = (m-1)/2; n < m; n++ ) {  
                coeffs[n] = 2. - 2.*n/(m-1);  
            }  
            break;  
            case 5: /* 4 term Blackman-Harris  */  
            double a0;  
            double a1;  
            double a2;  
            double a3;  
 
            a0 = 0.35875;  
            a1 = 0.48829;  
            a2 = 0.14128;  
            a3 = 0.01168;  
 
            for( n = 0; n < m; n++ ) {  
                coeffs[n] = a0 - a1* Math.cos(twopi*(double)(n+0.5)/m) +  
                             a2*Math.cos(twopi*2.*(double)(n+0.5)/m) -  
                             a3*Math.cos(twopi*3.*(double)(n+0.5)/m);  
            }  
            break;  
            default:  
            break;  
        }  
        return coeffs;  
    }  
}

6.5.8 YScale.java

 
import java.awt.*;  
import javax.swing.*;  
import java.awt.event.*;  
 
/**  
 * Esta clase maneja la escala del eje Y (potencia).  
 *  
 * @author Vicente González Ruiz  
 * @version 28-Mayo-2004  
 */  
public class YScale {  
    private JFrame frame;  
    private JPanel panel;  
    private JLabel scaleLabel, stepLabel;  
 
    /* Factor de escala en el eje Y */  
    private double scale, step;  
 
    class ScaleControl extends JLabel implements MouseWheelListener {  
 
        ScaleControl(String str, int horizontalAlignment){  
            super(str,horizontalAlignment);  
            super.addMouseWheelListener(this);  
        }  
 
        public void mouseWheelMoved(MouseWheelEvent e) {  
            int wheelRotationDir = e.getWheelRotation();  
            if(wheelRotationDir < 0) {  
                scale -= step;  
                if(scale <= 1.0) scale = 1.0;  
            } else {  
                scale += step;  
            }  
            this.setText("Scale = " + scale);  
        }  
    }  
 
    class StepControl extends JPanel implements ActionListener {  
 
        String initialStepString;  
        String x100String = "Step = 100.0";  
        String x10String = "Step = 10.0";  
        String x1String = "Step = 1.0";  
 
        double initialStep;  
 
        StepControl(double initialStep) {  
            this.initialStep = initialStep;  
            Double initialStepObj = new Double(initialStep);  
            initialStepString = new String("Step = " + initialStepObj.toString());  
 
            // 1. Creating the radio buttons  
            JRadioButton initialStepButton = new JRadioButton(initialStepString);  
            initialStepButton.setActionCommand(initialStepString);  
            initialStepButton.setSelected(true);  
 
            JRadioButton x100Button = new JRadioButton(x100String);  
            x100Button.setActionCommand(x100String);  
            //x100Button.setMnemonic(KeyEvent.VK_A);  
            //x100Button.setSelected(true);  
 
            JRadioButton x10Button = new JRadioButton(x10String);  
            x10Button.setActionCommand(x10String);  
 
            JRadioButton x1Button = new JRadioButton(x1String);  
            x1Button.setActionCommand(x1String);  
 
            // 2. Grouping the radio buttons  
            ButtonGroup group = new ButtonGroup();  
            group.add(initialStepButton);  
            group.add(x100Button);  
            group.add(x10Button);  
            group.add(x1Button);  
 
            // 3. Registering a listener for the radio buttons  
            initialStepButton.addActionListener(this);  
            x100Button.addActionListener(this);  
            x10Button.addActionListener(this);  
            x1Button.addActionListener(this);  
 
            // 4. Put the radio buttons in a column in a panel  
            JPanel radioPanel = new JPanel(new GridLayout(0,1));  
            radioPanel.add(initialStepButton);  
            radioPanel.add(x100Button);  
            radioPanel.add(x10Button);  
            radioPanel.add(x1Button);  
 
            add(radioPanel, BorderLayout.LINE_START);  
            setBorder(BorderFactory.createEmptyBorder(10,10,10,10));  
        }  
 
        // Listens to the radio buttons  
        public void actionPerformed(ActionEvent e) {  
            String choice = e.getActionCommand();  
            if(choice == x100String) {  
                step = 100;  
            } else if(choice == x10String) {  
                step = 10;  
            } else if(choice == x10String) {  
                step = 1;  
            } else {  
                step = initialStep;  
            }  
        }  
    }  
    /**  
     * Constructor for objects of class YScale  
     */  
    public YScale(double scale, double step/*, JFrame panel*/) {  
        this.scale = scale;  
        this.step = step;  
 
        // Create and set up the window  
        frame = new JFrame("RTASA - Y-Scale Control");  
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
        frame.setSize(100,50);  
 
        // Create and set up the panel  
        panel = new JPanel(new GridLayout(1,0));  
 
        // Create and add the widgets  
        Double scaleObj = new Double(scale);  
        Double stepObj = new Double(step);  
        ScaleControl scaleLabel2 = new ScaleControl("Scale = " + scaleObj.toString(), SwingConstants.LEFT);  
        StepControl stepLabel = new StepControl(step);  
        panel.add(scaleLabel2);  
        panel.add(stepLabel);  
 
        // Add the panel to the window  
        frame.getContentPane().add(panel, BorderLayout.CENTER);  
 
        // Display the window  
        frame.pack();  
        frame.setVisible(true);  
 
        //Register for mouse-wheel events on the text area.  
        //scaleLabel.addMouseWheelListener(this);  
    }  
 
    /**  
     * An example of a method - replace this comment with your own  
     *  
     * @param  y   a sample parameter for a method  
     * @return     the sum of x and y  
     */  
    public double getScale() {  
        return scale;  
    }  
 
}