/* * Copyright (C) 2012 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.fingerprint; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import com.musicg.dsp.Resampler; import com.musicg.processor.TopManyPointsProcessorChain; import com.musicg.properties.FingerprintProperties; import com.musicg.wave.Wave; import com.musicg.wave.WaveHeader; import com.musicg.wave.extension.Spectrogram; /** * Audio fingerprint manager, handle fingerprint operations * * @author jacquet * */ public class FingerprintManager{ private FingerprintProperties fingerprintProperties=FingerprintProperties.getInstance(); private int sampleSizePerFrame=fingerprintProperties.getSampleSizePerFrame(); private int overlapFactor=fingerprintProperties.getOverlapFactor(); private int numRobustPointsPerFrame=fingerprintProperties.getNumRobustPointsPerFrame(); private int numFilterBanks=fingerprintProperties.getNumFilterBanks(); /** * Constructor */ public FingerprintManager(){ } /** * Extract fingerprint from Wave object * * @param wave Wave Object to be extracted fingerprint * @return fingerprint in bytes */ public byte[] extractFingerprint(Wave wave){ int[][] coordinates; // coordinates[x][0..3]=y0..y3 byte[] fingerprint=new byte[0]; // resample to target rate Resampler resampler=new Resampler(); int sourceRate = wave.getWaveHeader().getSampleRate(); int targetRate = fingerprintProperties.getSampleRate(); byte[] resampledWaveData=resampler.reSample(wave.getBytes(), wave.getWaveHeader().getBitsPerSample(), sourceRate, targetRate); // update the wave header WaveHeader resampledWaveHeader=wave.getWaveHeader(); resampledWaveHeader.setSampleRate(targetRate); // make resampled wave Wave resampledWave=new Wave(resampledWaveHeader,resampledWaveData); // end resample to target rate // get spectrogram's data Spectrogram spectrogram=resampledWave.getSpectrogram(sampleSizePerFrame, overlapFactor); double[][] spectorgramData=spectrogram.getNormalizedSpectrogramData(); List<Integer>[] pointsLists=getRobustPointList(spectorgramData); int numFrames=pointsLists.length; // prepare fingerprint bytes coordinates=new int[numFrames][numRobustPointsPerFrame]; for (int x=0; x<numFrames; x++){ if (pointsLists[x].size()==numRobustPointsPerFrame){ Iterator<Integer> pointsListsIterator=pointsLists[x].iterator(); for (int y=0; y<numRobustPointsPerFrame; y++){ coordinates[x][y]=pointsListsIterator.next(); } } else{ // use -1 to fill the empty byte for (int y=0; y<numRobustPointsPerFrame; y++){ coordinates[x][y]=-1; } } } // end make fingerprint // for each valid coordinate, append with its intensity List<Byte> byteList=new LinkedList<Byte>(); for (int i=0; i<numFrames; i++){ for (int j=0; j<numRobustPointsPerFrame; j++){ if (coordinates[i][j]!=-1){ // first 2 bytes is x int x=i; byteList.add((byte)(x>>8)); byteList.add((byte)x); // next 2 bytes is y int y=coordinates[i][j]; byteList.add((byte)(y>>8)); byteList.add((byte)y); // next 4 bytes is intensity int intensity=(int)(spectorgramData[x][y]*Integer.MAX_VALUE); // spectorgramData is ranged from 0~1 byteList.add((byte)(intensity>>24)); byteList.add((byte)(intensity>>16)); byteList.add((byte)(intensity>>8)); byteList.add((byte)intensity); } } } // end for each valid coordinate, append with its intensity fingerprint=new byte[byteList.size()]; Iterator<Byte> byteListIterator=byteList.iterator(); int pointer=0; while(byteListIterator.hasNext()){ fingerprint[pointer++]=byteListIterator.next(); } return fingerprint; } /** * Get bytes from fingerprint file * * @param fingerprintFile fingerprint filename * @return fingerprint in bytes */ public byte[] getFingerprintFromFile(String fingerprintFile){ byte[] fingerprint=null; try { InputStream fis=new FileInputStream(fingerprintFile); fingerprint=getFingerprintFromInputStream(fis); fis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return fingerprint; } /** * Get bytes from fingerprint inputstream * * @param fingerprintFile fingerprint inputstream * @return fingerprint in bytes */ public byte[] getFingerprintFromInputStream(InputStream inputStream){ byte[] fingerprint=null; try { fingerprint = new byte[inputStream.available()]; inputStream.read(fingerprint); } catch (IOException e) { e.printStackTrace(); } return fingerprint; } /** * Save fingerprint to a file * * @param fingerprint fingerprint bytes * @param filename fingerprint filename * @see fingerprint file saved */ public void saveFingerprintAsFile(byte[] fingerprint, String filename){ FileOutputStream fileOutputStream; try { fileOutputStream = new FileOutputStream(filename); fileOutputStream.write(fingerprint); fileOutputStream.close(); } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // robustLists[x]=y1,y2,y3,... private List<Integer>[] getRobustPointList(double[][] spectrogramData){ int numX=spectrogramData.length; int numY=spectrogramData[0].length; double[][] allBanksIntensities=new double[numX][numY]; int bandwidthPerBank=numY/numFilterBanks; for (int b=0; b<numFilterBanks; b++){ double[][] bankIntensities=new double[numX][bandwidthPerBank]; for (int i=0; i<numX; i++){ for (int j=0; j<bandwidthPerBank; j++){ bankIntensities[i][j]=spectrogramData[i][j+b*bandwidthPerBank]; } } // get the most robust point in each filter bank TopManyPointsProcessorChain processorChain=new TopManyPointsProcessorChain(bankIntensities,1); double[][] processedIntensities=processorChain.getIntensities(); for (int i=0; i<numX; i++){ for (int j=0; j<bandwidthPerBank; j++){ allBanksIntensities[i][j+b*bandwidthPerBank]=processedIntensities[i][j]; } } } List<int[]> robustPointList=new LinkedList<int[]>(); // find robust points for (int i=0; i<allBanksIntensities.length; i++){ for (int j=0; j<allBanksIntensities[i].length; j++){ if (allBanksIntensities[i][j]>0){ int[] point=new int[]{i,j}; //System.out.println(i+","+frequency); robustPointList.add(point); } } } // end find robust points List<Integer>[] robustLists=new LinkedList[spectrogramData.length]; for (int i=0; i<robustLists.length; i++){ robustLists[i]=new LinkedList<Integer>(); } // robustLists[x]=y1,y2,y3,... Iterator<int[]> robustPointListIterator=robustPointList.iterator(); while (robustPointListIterator.hasNext()){ int[] coor=robustPointListIterator.next(); robustLists[coor[0]].add(coor[1]); } // return the list per frame return robustLists; } /** * Number of frames in a fingerprint * Each frame lengths 8 bytes * Usually there is more than one point in each frame, so it cannot simply divide the bytes length by 8 * Last 8 byte of thisFingerprint is the last frame of this wave * First 2 byte of the last 8 byte is the x position of this wave, i.e. (number_of_frames-1) of this wave * * @param fingerprint fingerprint bytes * @return number of frames of the fingerprint */ public static int getNumFrames(byte[] fingerprint){ if (fingerprint.length<8){ return 0; } // get the last x-coordinate (length-8&length-7)bytes from fingerprint int numFrames=((int)(fingerprint[fingerprint.length-8]&0xff)<<8 | (int)(fingerprint[fingerprint.length-7]&0xff))+1; return numFrames; } }