Main Page   Class Hierarchy   Compound List   File List   Compound Members  

AvgChroma.java

00001 /*
00002  *  Copyright 2006 Columbia University.
00003  *
00004  *  This file is part of MEAPsoft.
00005  *
00006  *  MEAPsoft is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License version 2 as
00008  *  published by the Free Software Foundation.
00009  *
00010  *  MEAPsoft is distributed in the hope that it will be useful, but
00011  *  WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  *  General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with MEAPsoft; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00018  *  02110-1301 USA
00019  *
00020  *  See the file "COPYING" for the text of the license.
00021  */
00022 
00023 package com.meapsoft.featextractors;
00024 
00025 import java.util.Arrays;
00026 
00027 import com.meapsoft.MEAPUtil;
00028 import com.meapsoft.RingMatrix;
00029 import com.meapsoft.STFT;
00030 
00040 public class AvgChroma extends FeatureExtractor {
00041         
00042     // earliest FFT bin to use
00043 static final int FIRSTBAND = 3;
00044 
00045   double[][] chromaWts;
00046     double[] linSpec;
00047   int N, outDim;
00048 
00049 // #define CHROMA_LOG2 (0.69314718055995)
00050 
00051   public double hz2octs(double fq) {
00052       return Math.log(fq / 440.0)/0.69314718055995;
00053   }
00054 
00055   // Default constructor 
00056   public AvgChroma() {
00057       //this(129, 12, 44100.0);
00058       //this(MEAPUtil.frameSize/2+1, 12, MEAPUtil.samplingRate);
00059       this(1025, 12, MEAPUtil.samplingRate);
00060   }
00061 
00062   public AvgChroma(int N, int outDim, double sampleRate) {
00063     this.N = N;
00064     this.outDim = outDim;
00065     linSpec = new double[N];
00066 
00067     chromaWts = new double[outDim][N];
00068 
00069     // Create the chroma inner products
00070 
00071     double bin2hz = sampleRate / (2*(N-1));
00072     for (int i = FIRSTBAND; i < N; ++i) {
00073         double tot = 0;
00074         // 1/12 = 1 semi = 0.083333
00075         //double binwidth = max(0.08333333, hz2octs(bin2hz*(i+1)) - hz2octs(bin2hz*(i-1)))/2;
00076         double binwidth = hz2octs(bin2hz*(i+1)) - hz2octs(bin2hz*(i-1));
00077         if ( binwidth < 0.083333)
00078             binwidth = 0.083333;
00079         binwidth /= 4;
00080         double binocts = hz2octs(bin2hz*i);
00081         // fade out bins above 1 kHz
00082         double binwt = 1.0;
00083         if (bin2hz*i > 1000)
00084             binwt = Math.exp( -(bin2hz*i - 1000)/500);
00085         for (int j = 0; j < outDim; ++j) {
00086             double bindelta = binocts - (((double)j)/outDim);
00087             bindelta = bindelta - Math.rint(bindelta);
00088             chromaWts[j][i] = binwt*Math.exp(-0.5*Math.pow(bindelta/binwidth,2));
00089             tot = tot + chromaWts[j][i];
00090         }
00091         // Normalize energy distribution from each fft bin
00092         for (int j = 0; j < outDim; ++j) {
00093             chromaWts[j][i] /= tot;
00094         }
00095     }
00096   }
00097 
00098 
00099   public double[] features(STFT stft, long startFrame, int length) {
00100       int boff = 0;
00101     double[] chromSpec = new double[boff + outDim];
00102     double[] curFrame;
00103     double sum = 0;
00104     double sum2 = 0;
00105 
00106     boolean recalculateSTFT = stft.getRows() != N;
00107     RingMatrix newstft = null;
00108     if(recalculateSTFT)
00109     {
00110         // keep the same number of frames as in stft
00111         newstft = STFT.getSTFT(stft.getSamples(startFrame, startFrame+length), (N-1)*2, MEAPUtil.frameSize);
00112         //System.out.println(length+", "+newstft.getRows()+": "+newstft.getColumns());
00113         length = newstft.getColumns();
00114     }    
00115 
00116     // intialize average to 0
00117     Arrays.fill(linSpec, 0);
00118 
00119     // collect average linear spectrum
00120     for(int frame=0; frame<length; frame++) {
00121 
00122         if(!recalculateSTFT)
00123             curFrame = stft.getFrame(startFrame+frame);
00124         else
00125             curFrame = newstft.getColumn(frame);
00126 
00127         if(curFrame != null)
00128             for(int band=0; band<linSpec.length; band++)
00129                 linSpec[band] += Math.pow(10,curFrame[band]/10) / length;
00130     }
00131 
00132 
00133     //    // convert log magnitude to linear magnitude for binning
00134     //for(int band=0; band<linSpec.length; band++)
00135     //  //linSpec[band] = Math.exp(linSpec[band]);
00136     //  linSpec[band] = Math.pow(10,linSpec[band]/10);
00137 
00138     // matrix multiply to find bins
00139     for(int bin=0; bin<outDim; bin++) {
00140         double val = 0;
00141         for(int band=FIRSTBAND; band<linSpec.length; band++) {
00142             val += linSpec[band] * chromaWts[bin][band];
00143         }
00144         chromSpec[boff+bin] = val;
00145         sum += val;
00146         sum2 += val*val;
00147     }
00148 
00149     // chroma vectors have unit norm
00150     double mean = sum/outDim;
00151     double sd = Math.sqrt( sum2/outDim - Math.pow(mean,2));
00152     double rms = Math.sqrt(sum2/outDim);
00153     for(int bin=0; bin<outDim; bin++) {
00154         //      chromSpec[boff+bin] = (chromSpec[boff+bin] - mean)/ sd;
00155         chromSpec[boff+bin] = chromSpec[boff+bin]/rms;
00156     }
00157 
00158 
00159     if (boff > 0) {
00160         // calculate complex average chroma
00161         double re = 0, im = 0;
00162         for (int bin = 0; bin < outDim; ++bin) {
00163             re = re + chromSpec[boff+bin] * Math.cos(6.28318531*bin/outDim);
00164             im = im + chromSpec[boff+bin] * Math.sin(6.28318531*bin/outDim);
00165         }
00166 
00167         double meanchrom = outDim * (Math.atan2(im, re) / 6.28318531);   
00168         // atan2 returns -pi..pi
00169         // fold back to +ve octave
00170         if (meanchrom < 0) 
00171             meanchrom += outDim;
00172 
00173         chromSpec[0] = meanchrom;
00174 
00175     }
00176     return chromSpec;
00177   }
00178 
00179 
00180         public String description()
00181         {
00182                 return "I am a generic FeatureExtractor";
00183         }
00184 }

Generated on Thu May 11 15:04:10 2006 for MEAPsoft by doxygen1.2.18