/**
* Xtreme Media Player a cross-platform media player.
* Copyright (C) 2005-2011 Besmir Beqiri
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package xtrememp.visualization;
import java.awt.Color;
import java.awt.Graphics2D;
import java.nio.FloatBuffer;
import xtrememp.player.dsp.DigitalSignalSynchronizer;
import xtrememp.player.dsp.DssContext;
import xtrememp.visualization.spectrum.Band;
import xtrememp.visualization.spectrum.BandDistribution;
import xtrememp.visualization.spectrum.BandGain;
import xtrememp.visualization.spectrum.FFT;
import xtrememp.visualization.spectrum.FlatBandGain;
import xtrememp.visualization.spectrum.FrequencyBandGain;
import xtrememp.visualization.spectrum.LinearBandDistribution;
import xtrememp.visualization.spectrum.LogBandDistribution;
import javax.sound.sampled.SourceDataLine;
/**
* Renders a spectrogram.
*
* Based on KJ-DSS project by Kristofer Fudalewski (http://sirk.sytes.net).
*
* @author Besmir Beqiri
*/
public final class Spectrogram extends Visualization {
public static final String NAME = "Spectrogram";
//
public static final BandDistribution BAND_DISTRIBUTION_LINEAR = new LinearBandDistribution();
public static final BandDistribution BAND_DISTRIBUTION_LOG = new LogBandDistribution(4, 20.0D);
public static final BandGain BAND_GAIN_FLAT = new FlatBandGain(4.0F);
public static final BandGain BAND_GAIN_FREQUENCY = new FrequencyBandGain(4.0F);
//
public static final BandDistribution DEFAULT_SPECTROGRAM_BAND_DISTRIBUTION = BAND_DISTRIBUTION_LINEAR;
public static final BandGain DEFAULT_SPECTROGRAM_BAND_GAIN = BAND_GAIN_FREQUENCY;
public static final float DEFAULT_SPECTRUM_ANALYSER_GAIN = 5.0F;
//
private BandDistribution bandDistribution;
private BandGain bandGain;
private Band[] bdTable;
private float[] bgTable;
private int bands;
private int fftSampleSize;
private float fftSampleRate;
private FFT fft;
private float gain;
private float bandWidth;
private float[] brgb;
private float[] frgb;
public Spectrogram() {
this.bandDistribution = DEFAULT_SPECTROGRAM_BAND_DISTRIBUTION;
this.bandGain = DEFAULT_SPECTROGRAM_BAND_GAIN;
this.gain = DEFAULT_SPECTRUM_ANALYSER_GAIN;
brgb = new float[3];
frgb = new float[3];
setBandCount(DigitalSignalSynchronizer.DEFAULT_SAMPLE_SIZE / 2);
}
/**
* Sets the numbers of bands rendered by the spectrogram.
*
* @param count Cannot be more than half the "FFT sample size".
*/
public synchronized void setBandCount(int count) {
bands = count;
computeBandTables();
}
private void computeBandTables() {
if (bands > 0 && fftSampleSize > 0 & fft != null) {
//Create band table.
bdTable = bandDistribution.create(bands, fft, fftSampleRate);
bands = bdTable.length;
//Create gain table.
bgTable = bandGain.create(fft, fftSampleRate);
}
}
@Override
public void init(int sampleSize, SourceDataLine sourceDataLine) {
this.fftSampleSize = sampleSize;
this.fftSampleRate = sourceDataLine.getFormat().getFrameRate();
this.fft = new FFT(fftSampleSize);
computeBandTables();
}
@Override
public String getDisplayName() {
return NAME;
}
@Override
public void setBackgroundColor(Color backgroundColor) {
super.setBackgroundColor(backgroundColor);
freeImage();
}
@Override
public void setForegroundColor(Color foregroundColor) {
super.setForegroundColor(foregroundColor);
freeImage();
}
@Override
public synchronized void render(DssContext dssContext, Graphics2D g2d, int width, int height) {
// FFT processing.
FloatBuffer[] channelsBuffer = dssContext.getDataNormalized();
float[] _fft = fft.calculate(channelsMerge(channelsBuffer));
bandWidth = (float) height / (float) bands;
// Rendering.
float y = height;
int width2 = width - 1;
int b, bd, i, li = 0, mi;
float fs, m;
// Group up available bands using band distribution table.
for (bd = 0; bd < bands; bd++) {
// Get band distribution entry.
i = bdTable[bd].distribution;
m = 0;
mi = 0;
// Find loudest band in group. (Group is from 'li' to 'i').
for (b = li; b < i; b++) {
float lf = _fft[b];
if (lf > m) {
m = lf;
mi = b;
}
}
li = i;
// Calculate gain using log, then static gain.
fs = (m * bgTable[mi]) * gain;
// Limit over-saturation.
if (fs > 1.0F) {
fs = 1.0F;
}
// Calculate spectrogram color shifting between foreground and background colors.
float _fs = 1.0F - fs;
backgroundColor.getColorComponents(brgb);
foregroundColor.getColorComponents(frgb);
Color color = new Color(frgb[0] * fs + brgb[0] * _fs,
frgb[1] * fs + brgb[1] * _fs,
frgb[2] * fs + brgb[2] * _fs);
g2d.setColor(color);
g2d.drawLine(width2, Math.round(y), width2, Math.round(y - bandWidth));
y -= bandWidth;
}
g2d.drawImage(buffImage, -1, 0, null);
}
}