package audio.output; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.Line2D; import java.io.Serializable; import java.util.Vector; import javax.sound.sampled.AudioFormat; import javax.swing.JPanel; import org.trianacode.gui.windows.ErrorDialog; import triana.types.audio.MultipleAudio; class WaveViewPanel extends JPanel implements Serializable { // for double buffering transient Dimension offDimension; transient Image offImage; transient Graphics offGraphics; private Font font10 = new Font("serif", Font.PLAIN, 10); private Font font12 = new Font("serif", Font.PLAIN, 12); Color jfcBlue = new Color(204, 204, 255); Color pink = new Color(255, 175, 175); int initialWidth; int initialHeight; double zoomFactor = 3.0; //data related Vector audioData = new Vector(); AudioFormat audioFormat; int dataLength; double duration, seconds = 0; int srate; int channels; double dataScale; /** * The space for the information line at the bottom of the image */ int XLAB = 35; // on the right int YLAB = 15; // at the bottom int xLabspace = 60; // variables which can be altered by the user Color foreground = jfcBlue; Color background = new Color(20, 20, 20); Color axis = pink; Color axisText = Color.black; // for efficient drawing of the waveform (i.e. only draw points viewable) boolean efficientDraw = true; int efficientViewFactor = 1; boolean painting = false; public WaveViewPanel() { super(); setBackground(background); setDoubleBuffered(false); setOpaque(true); } /** * resets the parameters ready for adduing new data */ public void initialize(MultipleAudio au) { audioFormat = au.getAudioFormat(); dataLength = au.getChannelLength(0); srate = au.getChannelFormat(0).getSamplingRate(); duration = ((double) dataLength) / (double) srate; channels = au.getChannels(); audioData.removeAllElements(); dataScale = Math.pow(2.0, (double) (audioFormat.getSampleSizeInBits() - 1)); } public void addWave(MultipleAudio au, int chan) { audioData.addElement(au.getChannel(chan)); } public void drawGraph() { painting = true; repaint(); } public void paintGraph(Graphics g) { Dimension d = getSize(); int w = d.width; int h = d.height; int graphAreaX = w - XLAB; int graphAreaY = (h - YLAB) / channels; // Graph Graphics2D g2 = (Graphics2D) g; g2.setBackground(background); g2.clearRect(0, 0, w, h); // axis border g2.setColor(axis); g2.fillRect(0, h - YLAB, w, h); g2.fillRect(w - XLAB, 0, w, h); // draw X axis labels ********************* g2.setFont(font10); g2.setColor(axisText); double secs = (int) (dataLength / srate); int totalTicks = w / xLabspace; double[] labels = null; try { labels = WaveViewLabelling.label(0.0, secs, totalTicks); } catch (Exception e) { System.out.println("Exception in x labelling"); } totalTicks = labels.length; // new length double newmax = labels[labels.length - 1]; double ratio = newmax / secs; double scalar = graphAreaX / secs; double xl; int len = labels.length; if (newmax > secs) { --len; } for (int i = 1; i < len; ++i) { xl = labels[i] * scalar; g2.drawLine((int) xl, h - YLAB, (int) xl, h - YLAB + 4); g2.drawString(String.valueOf(labels[i]), (int) xl - 6, h - 2); } g2.setColor(foreground); // calculate graph area; double rec_srate = 1.0 / (double) srate; double xmove; if (efficientDraw) { xmove = 1.0; } else { xmove = (double) ((double) graphAreaX / (double) dataLength); } double samplesPerPixel = (double) dataLength / (double) graphAreaX; double xskip; if (efficientDraw) { xskip = samplesPerPixel; } else { xskip = 1.0; } double y_new; double x_new; double ystart = 0.0; double yoff; Object dataobj; int p; int max, min; short da; double y_zero = ((double) graphAreaY) / 2.0; double y_last = y_zero; double halfxskip = xskip / 2; double yscaler = (double) ((double) (graphAreaY / 2.0) / dataScale); // for y labels : for shorts double oneLabelStart = 30000.0 * yscaler; double oneLabelInc = 10000.0 * yscaler; int labs = 7; if (channels > 1) { // don't show 30,000 for stereo labs = 5; oneLabelStart = 20000.0 * yscaler; } int pos; for (int chan = 0; chan < channels; ++chan) { // for each channel // draw Y axis labels ********************* for shorts for now... g2.setColor(axisText); double start = ystart + y_zero + oneLabelStart; int text = -30000; if (channels > 1) { text = -20000; } for (int lab = 0; lab < labs; ++lab) { g2.drawLine(w - XLAB, (int) start, w - XLAB + 2, (int) start); g2.drawString(String.valueOf(text), w - XLAB + 5, (int) start + 4); start -= oneLabelInc; text += 10000; } g2.setColor(foreground); double x = 0.0; dataobj = audioData.get(chan); if (dataobj instanceof short[]) { short[] data = (short[]) dataobj; x_new = -xmove; yoff = ystart + y_zero; y_last = yoff; for (double i = 0.0; i < dataLength; i += xskip) { da = data[(int) i]; y_new = yoff - (da * yscaler); x_new += xmove; min = da; max = da; for (p = (int) (i - halfxskip); p >= 0 && p < dataLength && p < (int) (i + halfxskip); ++p) { max = Math.max(max, data[p]); min = Math.min(min, data[p]); } if (xskip > 1) { g2.drawLine((int) x, (int) (yoff - (min * yscaler)), (int) x, (int) (yoff - (max * yscaler))); } else { g2.drawLine((int) x, (int) y_last, (int) x_new, (int) y_new); } y_last = y_new; x = x_new; } } // end of short[] ystart += graphAreaY; } // end for each channel // .. draw current position .. if (seconds != 0) { double loc = seconds / duration * w; g2.setColor(pink); g2.setStroke(new BasicStroke(3)); g2.draw(new Line2D.Double(loc, 0, loc, h - 2)); } painting = false; } public void paintComponent(Graphics g) { super.paintComponent(g); Dimension d = getSize(); if ((d.height < 0) || (d.width < 0)) { return; } if (offGraphics == null) { offDimension = d; offImage = null; int to = 0; while (offImage == null) { try { System.out.println("Creating buffer for graphics"); offImage = createImage(d.width, d.height); System.runFinalization(); System.gc(); } catch (OutOfMemoryError e) { System.runFinalization(); System.gc(); ++to; if (to > 10) { ErrorDialog.show("Out Of Memory ! WaveView unable to Allocate Double Buffer"); setSize((int) (d.width / zoomFactor), (int) (d.height / zoomFactor)); return; } } } offGraphics = offImage.getGraphics(); paintGraph(offGraphics); } else if ((d.width != offDimension.width) || (d.height != offDimension.height)) { offDimension = d; offGraphics.dispose(); offImage.flush(); offImage = null; offGraphics = null; System.runFinalization(); System.gc(); int to = 0; while (offImage == null) { try { System.out.println("Change in size, recreated buffer"); offImage = createImage(d.width, d.height); } catch (OutOfMemoryError e) { offGraphics = null; offImage = null; System.runFinalization(); System.gc(); System.out.println("Out OF MEMORY ERROR!!!!, Trying again"); ++to; if (to > 10) { ErrorDialog.show("Out Of Memory ! WaveView unable to Zoom Further"); setSize((int) (d.width / zoomFactor), (int) (d.height / zoomFactor)); return; } } } offGraphics = offImage.getGraphics(); paintGraph(offGraphics); } else if (painting) { System.out.println("Forced Painting"); paintGraph(offGraphics); } if ((g != null) && (offImage != null)) { g.drawImage(offImage, 0, 0, this); } } /** * Zooms in the image by making the canvas larger */ public void zoomIn(int width, int height) { Dimension d = getSize(); int w = d.width; setSize((int) ((double) w * zoomFactor), height); drawGraph(); } /** * Zooms out the image by making the canvas smaller */ public void zoomOut(int width, int height) { Dimension d = getSize(); int w = d.width; int newX = (int) ((double) w * (1 / zoomFactor)); if (newX < width) { newX = width; } setSize(newX, height); drawGraph(); } /** * Zooms out fully */ public void fullSize(int width, int height) { setSize(width, height); drawGraph(); } public void detail() { efficientDraw = !efficientDraw; drawGraph(); } public boolean getDetail() { return efficientDraw; } }