00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 package com.meapsoft.featextractors;
00024
00025 import java.util.Arrays;
00026
00027 import com.meapsoft.MEAPUtil;
00028 import com.meapsoft.FeatExtractor;
00029 import com.meapsoft.RingMatrix;
00030 import com.meapsoft.STFT;
00031
00041 public class AvgChroma extends FeatureExtractor {
00042
00043
00044 static final int FIRSTBAND = 3;
00045
00046 protected double[][] chromaWts;
00047 protected double[] linSpec;
00048 protected int N, outDim;
00049
00050
00051
00052 public double hz2octs(double fq) {
00053 return Math.log(fq / 440.0)/0.69314718055995;
00054 }
00055
00056
00057 public AvgChroma() {
00058 this(FeatExtractor.nfft/2+1, 12, FeatExtractor.feSamplingRate);
00059 }
00060
00061 public AvgChroma(int N, int outDim, double sampleRate) {
00062 this.N = N;
00063 this.outDim = outDim;
00064 linSpec = new double[N];
00065
00066 chromaWts = new double[outDim][N];
00067
00068
00069
00070 double bin2hz = sampleRate / (2*(N-1));
00071 for (int i = FIRSTBAND; i < N; ++i) {
00072 double tot = 0;
00073
00074
00075 double binwidth = hz2octs(bin2hz*(i+1)) - hz2octs(bin2hz*(i-1));
00076 if ( binwidth < 0.083333)
00077 binwidth = 0.083333;
00078 binwidth /= 4;
00079 double binocts = hz2octs(bin2hz*i);
00080
00081 double binwt = 1.0;
00082 if (bin2hz*i > 1000)
00083 binwt = Math.exp( -(bin2hz*i - 1000)/500);
00084 for (int j = 0; j < outDim; ++j) {
00085 double bindelta = binocts - (((double)j)/outDim);
00086 bindelta = bindelta - Math.rint(bindelta);
00087 chromaWts[j][i] = binwt*Math.exp(-0.5*Math.pow(bindelta/binwidth,2));
00088 tot = tot + chromaWts[j][i];
00089 }
00090
00091 for (int j = 0; j < outDim; ++j) {
00092 chromaWts[j][i] /= tot;
00093 }
00094 }
00095 }
00096
00097
00098 public double[] features(STFT stft, long startFrame, int length) {
00099 int boff = 0;
00100 double[] chromSpec = new double[boff + outDim];
00101 double[] curFrame;
00102 double sum = 0;
00103 double sum2 = 0;
00104
00105 boolean recalculateSTFT = stft.getRows() != N;
00106 RingMatrix newstft = null;
00107 if(recalculateSTFT)
00108 {
00109
00110 newstft = STFT.getSTFT(stft.getSamples(startFrame, startFrame+length), (N-1)*2, stft.nhop);
00111 length = newstft.getColumns();
00112 }
00113
00114
00115 Arrays.fill(linSpec, 0);
00116
00117
00118 for(int frame=0; frame<length; frame++) {
00119
00120 if(!recalculateSTFT)
00121 curFrame = stft.getFrame(startFrame+frame);
00122 else
00123 curFrame = newstft.getColumn(frame);
00124
00125 if(curFrame != null)
00126 for(int band=0; band<linSpec.length; band++)
00127 linSpec[band] += Math.pow(10,curFrame[band]/10) / length;
00128 }
00129
00130
00131
00132
00133
00134
00135
00136
00137 for(int bin=0; bin<outDim; bin++) {
00138 double val = 0;
00139 for(int band=FIRSTBAND; band<linSpec.length; band++) {
00140 val += linSpec[band] * chromaWts[bin][band];
00141 }
00142 chromSpec[boff+bin] = val;
00143 sum += val;
00144 sum2 += val*val;
00145 }
00146
00147
00148 double mean = sum/outDim;
00149 double sd = Math.sqrt( sum2/outDim - Math.pow(mean,2));
00150 double rms = Math.sqrt(sum2/outDim);
00151 for(int bin=0; bin<outDim; bin++) {
00152
00153 chromSpec[boff+bin] = chromSpec[boff+bin]/rms;
00154 }
00155
00156
00157 if (boff > 0) {
00158
00159 double re = 0, im = 0;
00160 for (int bin = 0; bin < outDim; ++bin) {
00161 re = re + chromSpec[boff+bin] * Math.cos(6.28318531*bin/outDim);
00162 im = im + chromSpec[boff+bin] * Math.sin(6.28318531*bin/outDim);
00163 }
00164
00165 double meanchrom = outDim * (Math.atan2(im, re) / 6.28318531);
00166
00167
00168 if (meanchrom < 0)
00169 meanchrom += outDim;
00170
00171 chromSpec[0] = meanchrom;
00172
00173 }
00174 return chromSpec;
00175 }
00176
00177
00178 public String description()
00179 {
00180 return "12-dimensional vector of energy distribution across each semitone of the octave.";
00181 }
00182 }