/* * Created on Mar 20, 2007 * * Copyright (c) 2006-2007 P.J.Leonard * * http://www.frinika.com * * This file is part of Frinika. * * Frinika 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 of the License, or * (at your option) any later version. * Frinika 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 * along with Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.frinika.audio.analysis.gui; /* * Draws a frequency plot with a keyboard (vertical slice of spectrogram) * * */ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Rectangle; import java.util.Observable; import com.frinika.audio.analysis.Mapper; import com.frinika.audio.analysis.OscillatorNode; import com.frinika.audio.analysis.SpectrogramDataListener; import com.frinika.audio.analysis.SpectrumDataBuilder; import com.frinika.audio.analysis.StaticSpectrogramSynth; public class SpectralSliceImage extends Observable implements CursorObserver, SpectrogramDataListener { /** * */ private static final long serialVersionUID = 1L; SpectrumDataBuilder spectroData; Mapper valMapper; Mapper freqMapper; long pixPtr = -1; int lastPix = -1; StaticSpectrogramSynth synth; private Rectangle renderRect; boolean isBlack[] = {false, true, false, false, true, false, true, false, false, true, false, true}; float pixToFreq[]; private float curBin; public SpectralSliceImage(SpectrumDataBuilder provider, Mapper valMapper, Mapper freqMapper, StaticSpectrogramSynth synth) { assert (freqMapper != null); this.spectroData = provider; this.freqMapper = freqMapper; this.valMapper = valMapper; this.synth = synth; // setBackground(Color.BLACK); } void setRect(Rectangle rect) { this.renderRect = rect; pixToFreq=new float[rect.height]; } public void drawImage(Graphics2D g, int x, int y) { // System.out.println("Graph Paint"); int w = renderRect.width; int h = renderRect.height; // top left; int xx = renderRect.x; int yy = renderRect.y; g.setColor(Color.BLACK); g.fillRect(xx, yy, w, h); // allow space for drawing piano keys. int hKey = h / 4; h -= hKey; g.setColor(Color.GREEN); float[] freq = spectroData.getFreqArray(); float[] magn = spectroData.getMagnitudeAt(pixPtr); // float[] magnX = spectroData.getSMagnitudeAt(pixPtr); if (magn == null) { g.drawString(" No data ... yet ", 10, 10); return; } // System.out.println("bot freq "+freq[0]); assert (magn != null); if (pixPtr < 0) { return; } float[] pFreq = spectroData.getPhaseFreqAt(pixPtr); int n = magn.length; double scaleX = w; double scaleY = h; double fbot = freq[0]; double ftop = freq[freq.length - 1]; double a0Freq = 55.0 / 2; double semi = Math.pow(2.0, 1.0 / 12.0); double qemi = Math.pow(2.0, 1.0 / 24.0); int note = 0; int octave = 0; double f1 = a0Freq * qemi / semi; double f2 = f1 * semi; for (; f1 < ftop; f1 *= semi, f2 *= semi) { if (f2 > fbot) { // boolean hiLite = f1 < freq[curBin] && f2 >= freq[curBin]; int xx1 = (int) (xx + scaleX * freqMapper.eval((float) f1)); int xx2 = (int) (xx + scaleX * freqMapper.eval((float) f2)); int hx = (int) ((xx2 - xx1) / 2.0); if (!isBlack[note]) { if (isBlack[(note + 1) % 12]) { xx2 += hx; } if (isBlack[(note + 11) % 12]) { xx1 -= hx; } g.setColor(Color.WHITE); g.fill3DRect(xx1, yy + h, xx2 - xx1, hKey, true); } // // char nc=(char) ('A' + note); // String str=String.format("%c", nc); } note = note + 1; if (note >= 12) { note = 0; octave = octave + 1; } } note = 0; octave = 0; f1 = a0Freq * qemi / semi; f2 = f1 * semi; for (; f1 < ftop; f1 *= semi, f2 *= semi) { if (f2 > fbot) { int xx1 = (int) (xx + scaleX * freqMapper.eval((float) f1)); int xx2 = (int) (xx + scaleX * freqMapper.eval((float) f2)); int hx = (int) ((xx2 - xx1) / 2.0); if (isBlack[note]) { g.setColor(Color.BLACK); g.fill3DRect(xx1, yy + h, xx2 - xx1, (int) (hKey * 0.55), true); } // // char nc=(char) ('A' + note); // String str=String.format("%c", nc); } note = note + 1; if (note >= 12) { note = 0; octave = octave + 1; } } // for (int j = 0; j < 2; j++) { int x1 = xx + (int) (freqMapper.eval(freq[0]) * scaleX); int y1 = yy + h - (int) (valMapper.eval(magn[0]) * scaleY); for (int i = 1; i < n; i++) { int x2 = xx + (int) (freqMapper.eval(freq[i]) * scaleX); int y2 = yy + h - (int) (valMapper.eval(magn[i]) * scaleY); // System.out.println(x1 + " " + y2); g.setColor(Color.GREEN); g.drawLine(x1, y1, x2, y2); if (pFreq != null) { int x3 = xx + (int) (freqMapper.eval(pFreq[i]) * scaleX); g.setColor(Color.RED); g.drawLine(x2, yy + h, x3, y2); } x1 = x2; y1 = y2; } // now the smooth version if (magn!= null) { x1 = xx + (int) (freqMapper.eval(freq[0]) * scaleX); y1 = yy + h - (int) (valMapper.eval(magn[0]) * scaleY); for (int i = 1; i < n; i++) { int x2 = xx + (int) (freqMapper.eval(freq[i]) * scaleX); int y2 = yy + h - (int) (valMapper.eval(magn[i]) * scaleY); // System.out.println(x1 + " " + y2); g.setColor(Color.RED); g.drawLine(x1, y1, x2, y2); if (pFreq != null) { int x3 = xx + (int) (freqMapper.eval(pFreq[i]) * scaleX); g.setColor(Color.RED); g.drawLine(x2, yy + h, x3, y2); } x1 = x2; y1 = y2; } } int pixlast = -100; // for (int i = 0; i < n; i++) { // int x2 = xx + (int) (freqMapper.eval(freq[i]) * scaleX); // // if (x2 - pixlast > 50) { // // g.setColor(Color.WHITE); // g.drawString(String.format(" %d ", (int) freq[i]), x2 - 2, yy + h - 2); // pixlast = x2; // } // } int i1=(int)curBin; if (i1 >= freq.length) i1=freq.length-1; int i2=i1+1; double fact2=curBin-i1; double fact1=1.0-fact2; int curX=xx + (int) (freqMapper.eval((float) (freq[i1]*fact1+freq[i2]*fact2) ) * scaleX); g.setColor(Color.WHITE); g.drawLine(curX, yy + h, curX, yy); if (synth != null) { for (OscillatorNode osc : synth.getOscillatorBank()) { if (!osc.active) { continue; } x1 = xx + (int) (freqMapper.eval((float) osc.getFreq()) * scaleX); y1 = yy + h - (int) (valMapper.eval((float) osc.getAmp()) * scaleY); g.setColor(Color.BLUE); g.drawLine(x1, yy + h, x1, y1); } } } public void notifyCursorChange(int pix,float curBin) { if (curBin == this.curBin && pixPtr == pix) { return; } pixPtr = pix; this.curBin=curBin; setChanged(); notifyObservers(); // repaint(); } public Dimension getPreferredSize() { return new Dimension(1000, 120); } public void notifySizeChange(Dimension d) { lastPix = -1; setChanged(); notifyObservers(); } public void notifyMoreDataReady() { int nextPix = spectroData.getChunkRenderedCount(); if ((nextPix < lastPix || lastPix < pixPtr) && nextPix >= pixPtr) { setChanged(); notifyObservers(); } lastPix = nextPix; } }