/* * Copyright (C) 2011 Jacquet Wong * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.musicg.wave.extension; import com.musicg.dsp.FastFourierTransform; import com.musicg.dsp.WindowFunction; import com.musicg.wave.Wave; /** * Handles the wave data in frequency-time domain. * * @author Jacquet Wong */ public class Spectrogram{ public static final int SPECTROGRAM_DEFAULT_FFT_SAMPLE_SIZE = 1024; public static final int SPECTROGRAM_DEFAULT_OVERLAP_FACTOR = 0; // 0 for no overlapping private Wave wave; private double[][] spectrogram; // relative spectrogram private double[][] absoluteSpectrogram; // absolute spectrogram private int fftSampleSize; // number of sample in fft, the value needed to be a number to power of 2 private int overlapFactor; // 1/overlapFactor overlapping, e.g. 1/4=25% overlapping private int numFrames; // number of frames of the spectrogram private int framesPerSecond; // frame per second of the spectrogram private int numFrequencyUnit; // number of y-axis unit private double unitFrequency; // frequency per y-axis unit /** * Constructor * * @param wave */ public Spectrogram(Wave wave) { this.wave=wave; // default this.fftSampleSize=SPECTROGRAM_DEFAULT_FFT_SAMPLE_SIZE; this.overlapFactor=SPECTROGRAM_DEFAULT_OVERLAP_FACTOR; buildSpectrogram(); } /** * Constructor * * @param wave * @param fftSampleSize number of sample in fft, the value needed to be a number to power of 2 * @param overlapFactor 1/overlapFactor overlapping, e.g. 1/4=25% overlapping, 0 for no overlapping */ public Spectrogram(Wave wave, int fftSampleSize, int overlapFactor) { this.wave=wave; if (Integer.bitCount(fftSampleSize)==1){ this.fftSampleSize=fftSampleSize; } else{ System.err.print("The input number must be a power of 2"); this.fftSampleSize=SPECTROGRAM_DEFAULT_FFT_SAMPLE_SIZE; } this.overlapFactor=overlapFactor; buildSpectrogram(); } /** * Build spectrogram */ private void buildSpectrogram(){ short[] amplitudes=wave.getSampleAmplitudes(); int numSamples = amplitudes.length; int pointer=0; // overlapping if (overlapFactor>1){ int numOverlappedSamples=numSamples*overlapFactor; int backSamples=fftSampleSize*(overlapFactor-1)/overlapFactor; int fftSampleSize_1=fftSampleSize-1; short[] overlapAmp= new short[numOverlappedSamples]; pointer=0; for (int i=0; i0){ numFrequencyUnit=absoluteSpectrogram[0].length; unitFrequency=(double)wave.getWaveHeader().getSampleRate()/2/numFrequencyUnit; // frequency could be caught within the half of nSamples according to Nyquist theory // normalization of absoultSpectrogram spectrogram=new double[numFrames][numFrequencyUnit]; // set max and min amplitudes double maxAmp=Double.MIN_VALUE; double minAmp=Double.MAX_VALUE; for (int i=0; imaxAmp){ maxAmp=absoluteSpectrogram[i][j]; } else if(absoluteSpectrogram[i][j]