/* * */ package xplayer.visualizer.model; import java.util.logging.Level; import java.util.logging.Logger; import javax.sound.sampled.SourceDataLine; import javafx.beans.property.SimpleIntegerProperty; import javafx.scene.paint.Color; import xplayer.dsp.KJDSPAudioDataConsumer; import xplayer.dsp.KJDigitalSignalProcessor; import xplayer.dsp.KJFFT; /** * This SuperClass represents the model of the Visualizer. * * @author GOXR3PLUS */ public class VisualizerModel extends ResizableCanvas implements KJDigitalSignalProcessor { /** The Constant log. */ private static final Logger logger = Logger.getLogger(VisualizerModel.class.getName()); /** * The width of the canvas */ public int canvasWidth = 0; /** * The height of the canvas */ public int canvasHeight = 0; /** * Half the height of the canvas */ public int halfCanvasHeight = 0; /** The left. */ protected float[] pLeftChannel = new float[1024]; /** The right. */ protected float[] pRightChannel = new float[1024]; /** The frame rate ratio hint. */ protected float frameRateRatioHint; /** The w sadfrr. */ private float wSadfrr; /** The w FFT. */ private float[] wFFT; /** The w fs. */ private float wFs; /** * The maximum that the display mode can reach */ public final static int DISPLAYMODE_MAXIMUM = DisplayMode.values().length - 2; //-1 cause i count from 0 /** The display mode. */ public final SimpleIntegerProperty displayMode = new SimpleIntegerProperty(Integer.parseInt(DisplayMode.SPRITE3D.toString())); /** The Constant DEFAULT_FPS. */ private static final int DEFAULT_FPS = 60; /** The Constant DEFAULT_SPECTRUM_ANALYSER_FFT_SAMPLE_SIZE. */ private static final int DEFAULT_SPECTRUM_ANALYSER_FFT_SAMPLE_SIZE = 512; /** The Constant DEFAULT_SPECTRUM_ANALYSER_BAND_COUNT. */ private static final int DEFAULT_SPECTRUM_ANALYSER_BAND_COUNT = 50; /** The Constant DEFAULT_SPECTRUM_ANALYSER_DECAY. */ private static final float DEFAULT_SPECTRUM_ANALYSER_DECAY = 0.05f; /** The Constant DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY. */ private static final int DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY = 20; /** The Constant DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO. */ private static final float DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO = 0.4f; /** The Constant DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO_RANGE. */ private static final float DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO_RANGE = 0.1f; /** The Constant MIN_SPECTRUM_ANALYSER_DECAY. */ private static final float MIN_SPECTRUM_ANALYSER_DECAY = 0.02f; /** The Constant MAX_SPECTRUM_ANALYSER_DECAY. */ private static final float MAX_SPECTRUM_ANALYSER_DECAY = 1.0f; /** The Constant DEFAULT_VU_METER_DECAY. */ private static final float DEFAULT_VU_METER_DECAY = 0.02f; /** The scope color. */ protected Color scopeColor; /** The spectrum analyser colors. */ static Color[] spectrumAnalyserColors = getDefaultSpectrumAnalyserColors(); /** The dsp. */ private KJDSPAudioDataConsumer dsp = null; /** The dsp has started. */ private boolean dspHasStarted = false; /** The peak color. */ protected Color peakColor = null; /** The peaks. */ protected int[] peaks = new int[DEFAULT_SPECTRUM_ANALYSER_BAND_COUNT]; /** The peaks delay. */ protected int[] peaksDelay = new int[DEFAULT_SPECTRUM_ANALYSER_BAND_COUNT]; /** The peak delay. */ protected int peakDelay = DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY; /** The peaks enabled. */ protected boolean peaksEnabled = true; /** The bar offset. */ protected int barOffset = 1; // -- Spectrum analyzer variables. /** The fft. */ protected KJFFT fft; /** The old FFT. */ protected float[] oldFFT; /** The sa FFT sample size. */ private int saFFTSampleSize; /** The sa bands. */ protected int saBands; /** The sa color scale. */ protected float saColorScale; /** The sa multiplier. */ protected float saMultiplier; /** The sa decay. */ protected float saDecay = DEFAULT_SPECTRUM_ANALYSER_DECAY; /** The source data line. */ protected SourceDataLine sourceDataLine = null; /** The old left. */ // -- VU Meter protected float oldLeft; /** The old right. */ protected float oldRight; /** The vu decay. */ protected float vuDecay = DEFAULT_VU_METER_DECAY; /** The vu color scale. */ protected float vuColorScale; /** The frames per second. */ // -- FPS calculations. protected int framesPerSecond = 0; /** The fps. */ public int fps = DEFAULT_FPS; /** The show FPS. */ public boolean showFPS = false; /** * Default Constructor. */ public VisualizerModel() { // ---------------------- setFramesPerSecond(DEFAULT_FPS); setPeakDelay((int) ( DEFAULT_FPS * DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO )); setSpectrumAnalyserFFTSampleSize(DEFAULT_SPECTRUM_ANALYSER_FFT_SAMPLE_SIZE); setSpectrumAnalyserDecay(DEFAULT_SPECTRUM_ANALYSER_DECAY); setSpectrumAnalyserBandCount(DEFAULT_SPECTRUM_ANALYSER_BAND_COUNT); setPeakColor(Color.WHITE); } /*----------------------------------------------------------------------- * * ----------------------------------------------------------------------- * * * Methods * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- */ /** * Called by the KJDigitalSignalProcessingAudioDataConsumer. * * @param pLeftChannel * Audio data for the left channel. * @param pRightChannel * Audio data for the right channel. * @param pFrameRateRatioHint * A float value representing the ratio of the current frame rate * to the desired frame rate. It is used to keep DSP animation * consistent if the frame rate drop below the desired frame * rate. */ @Override public synchronized void process(float[] pLeftChannel , float[] pRightChannel , float pFrameRateRatioHint) { this.pLeftChannel = pLeftChannel; this.pRightChannel = pRightChannel; this.frameRateRatioHint = pFrameRateRatioHint; } /** * Setup DSP. * * @param line * the new up DSP */ public void setupDSP(SourceDataLine line) { if (dsp != null) { // Number of Channels dsp.setChannelMode( line.getFormat().getChannels() == 1 ? KJDSPAudioDataConsumer.ChannelMode.MONO : KJDSPAudioDataConsumer.ChannelMode.STEREO); // SampleSizeInBits dsp.setSampleType(line.getFormat().getSampleSizeInBits() == 8 ? KJDSPAudioDataConsumer.SampleType.EIGHT_BIT : KJDSPAudioDataConsumer.SampleType.SIXTEEN_BIT); } } /** * Starts DSP. * * @param line * the line */ public void startDSP(SourceDataLine line) { if (line != null) sourceDataLine = line; // dsp null? if (dsp == null) { dsp = new KJDSPAudioDataConsumer(2048, fps); dsp.add(this); } if (sourceDataLine != null) { if (dspHasStarted) stopDSP(); dsp.start(sourceDataLine); dspHasStarted = true; logger.info("DSP started"); } } /** * Stop DSP. */ public void stopDSP() { if (dsp != null) { dsp.stop(); dspHasStarted = false; logger.setLevel(Level.INFO); logger.info("DSP stopped"); } } /** * Close DSP. */ public void closeDSP() { if (dsp != null) { stopDSP(); dsp = null; logger.info("DSP CLOSSED"); } } /** * Write PCM data to DSP. * * @param pcmdata * the pcmdata */ public void writeDSP(byte[] pcmdata) { if (dsp != null) dsp.writeAudioData(pcmdata); } /** * Clears the Canvas from the Previous Painting. */ public void clear() { gc.clearRect(0, 0, getWidth(), getHeight()); } /*----------------------------------------------------------------------- * * * ----------------------------------------------------------------------- * * * ----------------------------------------------------------------------- * * * GETTERS * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- */ /** * Return DSP. * * @return KJDSPAudioDataConsumer */ public KJDSPAudioDataConsumer getDSP() { return dsp; } /** * Checks if is peaks enabled. * * @return true, if is peaks enabled */ public boolean isPeaksEnabled() { return peaksEnabled; } /** * Gets the frames per second. * * @return the frames per second */ public int getFramesPerSecond() { return fps; } /** * Return peak fall off delay. * * @return peak fall off delay */ public int getPeakDelay() { return peakDelay; } /** * Gets the visualizer width. * * @return the visualizer width */ public int getVisualizerWidth() { return canvasWidth; } /** * Gets the visualizer height. * * @return the visualizer height */ public int getVisualizerHeight() { return canvasHeight; } /** * Gets the default spectrum analyzer colors. Colors are starting from green * and ending to red. * * @return the default spectrum analyzer colors */ public static Color[] getDefaultSpectrumAnalyserColors() { Color[] wColors = new Color[256]; for (int a = 0; a < 128; a++) wColors[a] = Color.rgb(0, ( a >> 1 ) + 192, 0); for (int a = 0; a < 64; a++) wColors[a + 128] = Color.rgb(a << 2, 255, 0); for (int a = 0; a < 64; a++) wColors[a + 192] = Color.rgb(255, 255 - ( a << 2 ), 0); return wColors; } /** * Gets the display mode. * * @return Returns the current display mode, DISPLAY_MODE_SCOPE or * DISPLAY_MODE_SPECTRUM_ANALYSER or DISPLAY_MODE_VUMETER. */ public synchronized int getDisplayMode() { return displayMode.get(); } /** * Gets the spectrum analyser band count. * * @return Returns the current number of bands displayed by the spectrum * analyser. */ public synchronized int getSpectrumAnalyserBandCount() { return saBands; } /** * Gets the spectrum analyser decay. * * @return Returns the decay rate of the spectrum analyser's bands. */ public synchronized float getSpectrumAnalyserDecay() { return saDecay; } /** * Gets the scope color. * * @return Returns the color the scope is rendered in. */ public synchronized Color getScopeColor() { return scopeColor; } /** * Gets the spectrum analyser colors. * * @return Returns the color scale used to render the spectrum analyser * bars. */ public synchronized Color[] getSpectrumAnalyserColors() { return spectrumAnalyserColors; } /** * Checks if is showing FPS. * * @return Returns 'true' if "Frames Per Second" are being calculated and * displayed. */ public boolean isShowingFPS() { return showFPS; } /** * Compute color scale. */ public void computeColorScale() { saColorScale = ( (float) spectrumAnalyserColors.length / canvasHeight ) * barOffset * 1.0f; vuColorScale = ( (float) spectrumAnalyserColors.length / ( canvasWidth - 32 ) ) * 2.0f; } /** * Compute SA multiplier. */ private void computeSAMultiplier() { saMultiplier = (float) ( ( saFFTSampleSize / 2.00 ) / saBands ); } /*----------------------------------------------------------------------- * * * ----------------------------------------------------------------------- * * * ----------------------------------------------------------------------- * * * SETTERS * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- */ /** * Sets the peaks enabled. * * @param peaksEnabled * the new peaks enabled */ public void setPeaksEnabled(boolean peaksEnabled) { this.peaksEnabled = peaksEnabled; } /** * Set visual peak color. * * @param c * the new peak color */ public void setPeakColor(Color c) { peakColor = c; } /** * Set peak fall off delay. * * @param waitFPS * the new peak delay */ public void setPeakDelay(int waitFPS) { int min = Math.round( ( DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO - DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO_RANGE ) * fps); int max = Math.round( ( DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO + DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO_RANGE ) * fps); if ( ( waitFPS >= min ) && ( waitFPS <= max )) { peakDelay = waitFPS; } else { peakDelay = Math.round(DEFAULT_SPECTRUM_ANALYSER_PEAK_DELAY_FPS_RATIO * fps); } } /** * Sets the frames per second. * * @param fps * the new frames per second */ public void setFramesPerSecond(int fps) { this.fps = fps; } /** * Sets the current display mode. * * @param pMode * the new display mode */ public synchronized void setDisplayMode(int pMode) { displayMode.set(pMode); } /** * Sets the color of the scope. * * @param pColor * the new scope color */ public synchronized void setScopeColor(Color pColor) { scopeColor = pColor; } /** * When 'true' is passed as a parameter, will overlay the "Frames Per * Seconds" achieved by the component. * * @param pState * the new show FPS */ public synchronized void setShowFPS(boolean pState) { showFPS = pState; } /** * Sets the numbers of bands rendered by the spectrum analyser. * * @param pCount * Cannot be more than half the "FFT sample size". */ public synchronized void setSpectrumAnalyserBandCount(int pCount) { saBands = pCount; peaks = new int[saBands]; peaksDelay = new int[saBands]; computeSAMultiplier(); } /** * Sets the spectrum analyzer band decay rate. * * @param pDecay * Must be a number between 0.0 and 1.0 exclusive. */ public synchronized void setSpectrumAnalyserDecay(float pDecay) { if ( ( pDecay >= MIN_SPECTRUM_ANALYSER_DECAY ) && ( pDecay <= MAX_SPECTRUM_ANALYSER_DECAY )) { saDecay = pDecay; } else saDecay = DEFAULT_SPECTRUM_ANALYSER_DECAY; } /** * Sets the spectrum analyzer color scale. * * @param pColors * Any amount of colors may be used. Must not be null. */ public synchronized void setSpectrumAnalyserColors(Color[] pColors) { spectrumAnalyserColors = pColors; computeColorScale(); } /** * Sets the FFT sample size to be just for calculating the spectrum analyzer * values. The default is 512. * * @param pSize * Cannot be more than the size of the sample provided by the * DSP. */ public synchronized void setSpectrumAnalyserFFTSampleSize(int pSize) { saFFTSampleSize = pSize; fft = new KJFFT(saFFTSampleSize); oldFFT = new float[saFFTSampleSize]; computeSAMultiplier(); } /** * Stereo merge. * * @param pLeft * the left * @param pRight * the right * @return A float[] array from merging left and right speakers */ public float[] stereoMerge(float[] pLeft , float[] pRight) { for (int a = 0; a < pLeft.length; a++) pLeft[a] = ( pLeft[a] + pRight[a] ) / 2.0f; return pLeft; } /** * Returns an array which has length<array length> and contains frequencies * in every cell which has a value from 0.00 to 1.00. * * @param pSample * the sample * @param arrayLength * the array length * @return An array which has length<array length> and contains frequencies * in every cell which has a value from 0.00 to 1.00. */ public float[] returnBandsArray(float[] pSample , int arrayLength) { wFFT = fft.calculate(pSample); wSadfrr = saDecay * frameRateRatioHint; wFs = 0; float[] array = new float[arrayLength]; for (int a = 0, band = 0; band < array.length; a += saMultiplier, band++) { wFs = 0; // -- Average out nearest bands. for (int b = 0; b < saMultiplier; b++) wFs += wFFT[a + b]; // -- Log filter. wFs = ( wFs = wFs * (float) Math.log(band + 2.00) ) > 1.0f ? 1.0f : wFs; // wFs = (wFs > 1.0f) ? 1.0f : wFs // -- Compute SA decay... if (wFs >= ( oldFFT[a] - wSadfrr )) oldFFT[a] = wFs; else { oldFFT[a] -= wSadfrr; if (oldFFT[a] < 0) oldFFT[a] = 0; wFs = oldFFT[a]; } array[band] = wFs; } return array; } /*----------------------------------------------------------------------- * * * ----------------------------------------------------------------------- * * * ----------------------------------------------------------------------- * * * GETTERS * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- * * ----------------------------------------------------------------------- */ /** * Visualizer Display Mode. * * @author GOXR3PLUS */ public enum DisplayMode { /** OSCILLOSCOPE */ OSCILLOSCOPE { @Override public String toString() { return "0"; } }, /** OSCILLOSCOPE */ STEREO_OSCILLOSCOPE { @Override public String toString() { return "1"; } }, /** OSCILLOSCOPE */ OSCILLOSCOPE_LINES { @Override public String toString() { return "2"; } }, /** The display spectrum bars. */ SPECTRUM_BARS { @Override public String toString() { return "3"; } }, /** Display a VOLUME_METER */ VOLUME_METER { @Override public String toString() { return "4"; } }, /** The display rosette with polyspiral. */ ROSETTE { @Override public String toString() { return "5"; } }, /** Display A Circle With Lines on it's circumference */ CIRCLE_WITH_LINES { @Override public String toString() { return "6"; } }, /** Display Sierpinski Triangles */ SIERPINSKI { @Override public String toString() { return "7"; } }, /** Display SPRITE3D */ SPRITE3D { @Override public String toString() { return "8"; } }, /** Display Julia Fractals */ JULIAFRACTALS { @Override public String toString() { return "9"; } } } }