From 02e81725356a01a65edd71b814d30147c3c5db62 Mon Sep 17 00:00:00 2001 From: Alexander James Wallar Date: Sun, 19 Jan 2014 13:06:53 -0500 Subject: [PATCH] Added Requests.java --- app/Locabean/AndroidManifest.xml | 2 +- app/Locabean/libs/musicg-1.4.2.0.jar | Bin 0 -> 71500 bytes .../locaudio/{locabean => io}/WaveWriter.java | 21 +- .../com/locaudio/locabean/NodeActivity.java | 7 +- .../src/com/locaudio/net/Requests.java | 83 ++ app/Locabean/src/com/musicg/api/ClapApi.java | 57 -- .../src/com/musicg/api/DetectionApi.java | 263 ------ .../src/com/musicg/api/WhistleApi.java | 56 -- .../com/musicg/dsp/FastFourierTransform.java | 65 -- .../com/musicg/dsp/LinearInterpolation.java | 67 -- .../src/com/musicg/dsp/Resampler.java | 88 --- .../src/com/musicg/dsp/WindowFunction.java | 99 --- .../experiment/math/cluster/Segment.java | 47 -- .../math/cluster/SegmentCluster.java | 83 -- .../fingerprint/FingerprintManager.java | 302 ------- .../fingerprint/FingerprintSimilarity.java | 106 --- .../FingerprintSimilarityComputer.java | 149 ---- .../com/musicg/fingerprint/PairManager.java | 240 ------ .../com/musicg/main/demo/FingerprintDemo.java | 46 -- .../main/demo/FingerprintRecognitionDemo.java | 73 -- .../src/com/musicg/main/demo/PitchDemo.java | 28 - .../src/com/musicg/main/demo/WaveDemo.java | 42 - .../com/musicg/main/demo/WhistleApiDemo.java | 31 - .../com/musicg/math/quicksort/QuickSort.java | 5 - .../math/quicksort/QuickSortDouble.java | 55 -- .../quicksort/QuickSortIndexPreserved.java | 22 - .../math/quicksort/QuickSortInteger.java | 55 -- .../musicg/math/quicksort/QuickSortShort.java | 55 -- .../com/musicg/math/rank/ArrayRankDouble.java | 138 ---- .../src/com/musicg/math/rank/MapRank.java | 7 - .../com/musicg/math/rank/MapRankDouble.java | 168 ---- .../com/musicg/math/rank/MapRankInteger.java | 168 ---- .../com/musicg/math/rank/MapRankShort.java | 169 ---- .../musicg/math/statistics/DataCentroid.java | 50 -- .../math/statistics/MathStatistics.java | 34 - .../src/com/musicg/math/statistics/Mean.java | 40 - .../math/statistics/SpectralCentroid.java | 50 -- .../math/statistics/StandardDeviation.java | 56 -- .../src/com/musicg/math/statistics/Sum.java | 46 -- .../math/statistics/ZeroCrossingRate.java | 63 -- .../src/com/musicg/pitch/PitchHandler.java | 90 --- .../musicg/processor/IntensityProcessor.java | 6 - .../com/musicg/processor/ProcessorChain.java | 31 - .../processor/RobustIntensityProcessor.java | 42 - .../TopManyPointsProcessorChain.java | 32 - .../properties/FingerprintProperties.java | 116 --- .../serialization/ObjectSerializer.java | 45 -- .../src/com/musicg/version/Version.java | 5 - app/Locabean/src/com/musicg/wave/Wave.java | 343 -------- .../src/com/musicg/wave/WaveFileManager.java | 101 --- .../src/com/musicg/wave/WaveHeader.java | 291 ------- .../src/com/musicg/wave/WaveTypeDetector.java | 82 -- .../extension/NormalizedSampleAmplitudes.java | 67 -- .../musicg/wave/extension/Spectrogram.java | 218 ----- app/Locabean/src/com/sun/media/sound/FFT.java | 748 ------------------ 55 files changed, 101 insertions(+), 5252 deletions(-) create mode 100644 app/Locabean/libs/musicg-1.4.2.0.jar rename app/Locabean/src/com/locaudio/{locabean => io}/WaveWriter.java (89%) create mode 100644 app/Locabean/src/com/locaudio/net/Requests.java delete mode 100644 app/Locabean/src/com/musicg/api/ClapApi.java delete mode 100644 app/Locabean/src/com/musicg/api/DetectionApi.java delete mode 100644 app/Locabean/src/com/musicg/api/WhistleApi.java delete mode 100644 app/Locabean/src/com/musicg/dsp/FastFourierTransform.java delete mode 100644 app/Locabean/src/com/musicg/dsp/LinearInterpolation.java delete mode 100644 app/Locabean/src/com/musicg/dsp/Resampler.java delete mode 100644 app/Locabean/src/com/musicg/dsp/WindowFunction.java delete mode 100644 app/Locabean/src/com/musicg/experiment/math/cluster/Segment.java delete mode 100644 app/Locabean/src/com/musicg/experiment/math/cluster/SegmentCluster.java delete mode 100644 app/Locabean/src/com/musicg/fingerprint/FingerprintManager.java delete mode 100644 app/Locabean/src/com/musicg/fingerprint/FingerprintSimilarity.java delete mode 100644 app/Locabean/src/com/musicg/fingerprint/FingerprintSimilarityComputer.java delete mode 100644 app/Locabean/src/com/musicg/fingerprint/PairManager.java delete mode 100644 app/Locabean/src/com/musicg/main/demo/FingerprintDemo.java delete mode 100644 app/Locabean/src/com/musicg/main/demo/FingerprintRecognitionDemo.java delete mode 100644 app/Locabean/src/com/musicg/main/demo/PitchDemo.java delete mode 100644 app/Locabean/src/com/musicg/main/demo/WaveDemo.java delete mode 100644 app/Locabean/src/com/musicg/main/demo/WhistleApiDemo.java delete mode 100644 app/Locabean/src/com/musicg/math/quicksort/QuickSort.java delete mode 100644 app/Locabean/src/com/musicg/math/quicksort/QuickSortDouble.java delete mode 100644 app/Locabean/src/com/musicg/math/quicksort/QuickSortIndexPreserved.java delete mode 100644 app/Locabean/src/com/musicg/math/quicksort/QuickSortInteger.java delete mode 100644 app/Locabean/src/com/musicg/math/quicksort/QuickSortShort.java delete mode 100644 app/Locabean/src/com/musicg/math/rank/ArrayRankDouble.java delete mode 100644 app/Locabean/src/com/musicg/math/rank/MapRank.java delete mode 100644 app/Locabean/src/com/musicg/math/rank/MapRankDouble.java delete mode 100644 app/Locabean/src/com/musicg/math/rank/MapRankInteger.java delete mode 100644 app/Locabean/src/com/musicg/math/rank/MapRankShort.java delete mode 100644 app/Locabean/src/com/musicg/math/statistics/DataCentroid.java delete mode 100644 app/Locabean/src/com/musicg/math/statistics/MathStatistics.java delete mode 100644 app/Locabean/src/com/musicg/math/statistics/Mean.java delete mode 100644 app/Locabean/src/com/musicg/math/statistics/SpectralCentroid.java delete mode 100644 app/Locabean/src/com/musicg/math/statistics/StandardDeviation.java delete mode 100644 app/Locabean/src/com/musicg/math/statistics/Sum.java delete mode 100644 app/Locabean/src/com/musicg/math/statistics/ZeroCrossingRate.java delete mode 100644 app/Locabean/src/com/musicg/pitch/PitchHandler.java delete mode 100644 app/Locabean/src/com/musicg/processor/IntensityProcessor.java delete mode 100644 app/Locabean/src/com/musicg/processor/ProcessorChain.java delete mode 100644 app/Locabean/src/com/musicg/processor/RobustIntensityProcessor.java delete mode 100644 app/Locabean/src/com/musicg/processor/TopManyPointsProcessorChain.java delete mode 100644 app/Locabean/src/com/musicg/properties/FingerprintProperties.java delete mode 100644 app/Locabean/src/com/musicg/serialization/ObjectSerializer.java delete mode 100644 app/Locabean/src/com/musicg/version/Version.java delete mode 100644 app/Locabean/src/com/musicg/wave/Wave.java delete mode 100644 app/Locabean/src/com/musicg/wave/WaveFileManager.java delete mode 100644 app/Locabean/src/com/musicg/wave/WaveHeader.java delete mode 100644 app/Locabean/src/com/musicg/wave/WaveTypeDetector.java delete mode 100644 app/Locabean/src/com/musicg/wave/extension/NormalizedSampleAmplitudes.java delete mode 100644 app/Locabean/src/com/musicg/wave/extension/Spectrogram.java delete mode 100644 app/Locabean/src/com/sun/media/sound/FFT.java diff --git a/app/Locabean/AndroidManifest.xml b/app/Locabean/AndroidManifest.xml index 84ea4e4..d135b9d 100644 --- a/app/Locabean/AndroidManifest.xml +++ b/app/Locabean/AndroidManifest.xml @@ -5,7 +5,7 @@ android:versionName="1.0" > diff --git a/app/Locabean/libs/musicg-1.4.2.0.jar b/app/Locabean/libs/musicg-1.4.2.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..c58c0af1ea215c755570824e11b7c34ea3089483 GIT binary patch literal 71500 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 nmVTv6{Z-n}{|)}z paramMap) + throws ClientProtocolException, IOException, JSONException { + HttpClient httpclient = new DefaultHttpClient(); + HttpPost httppost = new HttpPost(this.url); + + // Request parameters and other properties. + List params = new ArrayList(); + + Iterator> it = paramMap.entrySet().iterator(); + Entry pair = null; + + while (it.hasNext()) { + pair = (Entry) it.next(); + params.add(new BasicNameValuePair(pair.getKey(), pair.getValue())); + } + + httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); + + // Execute and get the response. + HttpResponse response = httpclient.execute(httppost); + HttpEntity entity = response.getEntity(); + + if (entity != null) { + InputStreamReader reader = new InputStreamReader( + entity.getContent()); + try { + CharBuffer target = CharBuffer.allocate(CHAR_BUFFER_SIZE); + reader.read(target); + return new JSONObject(target.toString()); + } finally { + reader.close(); + } + } else { + return new JSONObject(); + } + } + + public JSONObject get(String... params) { + return new JSONObject(); + } +} diff --git a/app/Locabean/src/com/musicg/api/ClapApi.java b/app/Locabean/src/com/musicg/api/ClapApi.java deleted file mode 100644 index 0937cda..0000000 --- a/app/Locabean/src/com/musicg/api/ClapApi.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.api; - -import com.musicg.wave.WaveHeader; - -/** - * Api for detecting clap - * - * @author Jacquet Wong - * - */ -public class ClapApi extends DetectionApi{ - - public ClapApi(WaveHeader waveHeader) { - super(waveHeader); - } - - protected void init(){ - // settings for detecting a clap - minFrequency = 1000.0f; - maxFrequency = Double.MAX_VALUE; - - // get the decay part of a clap - minIntensity = 10000.0f; - maxIntensity = 100000.0f; - - minStandardDeviation = 0.0f; - maxStandardDeviation = 0.05f; - - highPass = 100; - lowPass = 10000; - - minNumZeroCross = 100; - maxNumZeroCross = 500; - - numRobust = 4; - } - - public boolean isClap(byte[] audioBytes){ - return isSpecificSound(audioBytes); - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/api/DetectionApi.java b/app/Locabean/src/com/musicg/api/DetectionApi.java deleted file mode 100644 index 723eb81..0000000 --- a/app/Locabean/src/com/musicg/api/DetectionApi.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 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.api; - -import com.musicg.math.rank.ArrayRankDouble; -import com.musicg.math.statistics.StandardDeviation; -import com.musicg.math.statistics.ZeroCrossingRate; -import com.musicg.wave.Wave; -import com.musicg.wave.WaveHeader; -import com.musicg.wave.extension.Spectrogram; - -/** - * Api for detecting different sounds - * - * @author Jacquet Wong - * - */ -public class DetectionApi { - - protected WaveHeader waveHeader; - protected int fftSampleSize; - protected int numFrequencyUnit; - protected double unitFrequency; - protected double minFrequency, maxFrequency; - protected double minIntensity, maxIntensity; - protected double minStandardDeviation, maxStandardDeviation; - protected int highPass, lowPass; - protected int minNumZeroCross, maxNumZeroCross; - protected int lowerBoundary, upperBoundary; - protected int numRobust; - - /** - * Constructor, support mono Wav only, 4096 sample byte size for 44100Hz - * 16bit mono wav - * - * @param sampleRate - * Sample rate of the input audio byte - * @param bitsPerSample - * Bit size of a sample of the input audio byte - */ - public DetectionApi(WaveHeader waveHeader) { - if (waveHeader.getChannels() == 1) { - this.waveHeader = waveHeader; - init(); - } else { - System.err.println("DetectionAPI supports mono Wav only"); - } - } - - /** - * Initiate the settings for specific sound detection - */ - protected void init(){ - // do nothing, needed to be overrided - } - - /** - * Determine the audio bytes contains a specific sound or not - * - * @param audioBytes - * input audio byte - * @return - */ - public boolean isSpecificSound(byte[] audioBytes) { - - int bytesPerSample = waveHeader.getBitsPerSample() / 8; - int numSamples = audioBytes.length / bytesPerSample; - - // numSamples required to be a power of 2 - if (numSamples > 0 && Integer.bitCount(numSamples) == 1) { - fftSampleSize = numSamples; - numFrequencyUnit = fftSampleSize / 2; - - // frequency could be caught within the half of nSamples according to Nyquist theory - unitFrequency = (double) waveHeader.getSampleRate() / 2 / numFrequencyUnit; - - // set boundary - lowerBoundary = (int) (highPass / unitFrequency); - upperBoundary = (int) (lowPass / unitFrequency); - // end set boundary - - Wave wave = new Wave(waveHeader, audioBytes); // audio bytes of this frame - short[] amplitudes = wave.getSampleAmplitudes(); - - // spectrum for the clip - Spectrogram spectrogram = wave.getSpectrogram(fftSampleSize, 0); - - double[][] spectrogramData = spectrogram.getAbsoluteSpectrogramData(); - - // since fftSampleSize==numSamples, there're only one spectrum which is thisFrameSpectrogramData[0] - double[] spectrum = spectrogramData[0]; - - int frequencyUnitRange = upperBoundary - lowerBoundary + 1; - double[] rangedSpectrum = new double[frequencyUnitRange]; - System.arraycopy(spectrum, lowerBoundary, rangedSpectrum, 0, rangedSpectrum.length); - - if (frequencyUnitRange <= spectrum.length) { - - if (isPassedIntensity(spectrum)){ - if (isPassedStandardDeviation(spectrogramData)){ - if (isPassedZeroCrossingRate(amplitudes)){ - if (isPassedFrequency(rangedSpectrum)){ - return true; - } - } - } - } - - /* - // run all checking for debug - boolean isPassedChecking = true; - // rule 1: check the intensity of this frame - isPassedChecking &= isPassedIntensity(spectrum); - // rule 2: check the frequency of this frame - isPassedChecking &= isPassedFrequency(rangedSpectrum); - // rule 3: check the zero crossing rate of this frame - isPassedChecking &= isPassedZeroCrossingRate(amplitudes); - // rule 4: check the standard deviation of this frame with reference of previous frames - isPassedChecking &= isPassedStandardDeviation(spectrogramData); - System.out.println("Result: " + isPassedChecking + "\n"); - return isPassedChecking; - // end run all checking for debug - */ - - } else { - System.err - .println("is error: the wave needed to be higher sample rate"); - } - - } else { - System.out.println("The sample size must be a power of 2"); - } - - return false; - } - - protected void normalizeSpectrogramData(double[][] spectrogramData) { - - // normalization of absoultSpectrogram - // set max and min amplitudes - double maxAmp = Double.MIN_VALUE; - double minAmp = Double.MAX_VALUE; - for (int i = 0; i < spectrogramData.length; i++) { - for (int j = 0; j < spectrogramData[i].length; j++){ - if (spectrogramData[i][j] > maxAmp) { - maxAmp = spectrogramData[i][j]; - } else if (spectrogramData[i][j] < minAmp) { - minAmp = spectrogramData[i][j]; - } - } - } - // end set max and min amplitudes - - // normalization - // avoiding divided by zero - double minValidAmp = 0.00000000001F; - if (minAmp == 0) { - minAmp = minValidAmp; - } - - double diff = Math.log10(maxAmp / minAmp); // perceptual difference - for (int i = 0; i < spectrogramData.length; i++) { - for (int j = 0; j < spectrogramData[i].length; j++) { - if (spectrogramData[i][j] < minValidAmp) { - spectrogramData[i][j] = 0; - } else { - spectrogramData[i][j] = (Math.log10(spectrogramData[i][j] / minAmp)) / diff; - } - } - } - // end normalization - } - - protected boolean isPassedStandardDeviation(double[][] spectrogramData){ - - // normalize the spectrogramData (with all frames in the spectrogram) - normalizeSpectrogramData(spectrogramData); - - // analyst data in this frame - // since fftSampleSize==numSamples, there're only one spectrum which is spectrogramData[last] - double[] spectrum = spectrogramData[spectrogramData.length - 1]; - // find top most robust frequencies in this frame - double[] robustFrequencies = new double[numRobust]; - ArrayRankDouble arrayRankDouble = new ArrayRankDouble(); - double nthValue = arrayRankDouble.getNthOrderedValue(spectrum, numRobust, false); - // end analyst data in this frame - - int count = 0; - for (int i = 0; i < spectrum.length; i++) { - if (spectrum[i] >= nthValue) { - robustFrequencies[count++] = spectrum[i]; - if (count >= numRobust) { - break; - } - } - } - // end find top most robust frequencies - - StandardDeviation standardDeviation = new StandardDeviation(); - standardDeviation.setValues(robustFrequencies); - double sd = standardDeviation.evaluate(); - - // range of standard deviation - boolean result = (sd >= minStandardDeviation && sd <= maxStandardDeviation); - //System.out.println("sd: " + sd + " " + result); - return result; - } - - protected boolean isPassedFrequency(double[] spectrum){ - // find the robust frequency - ArrayRankDouble arrayRankDouble = new ArrayRankDouble(); - double robustFrequency = arrayRankDouble.getMaxValueIndex(spectrum) * unitFrequency; - - // frequency of the sound should not be too low or too high - boolean result = (robustFrequency >= minFrequency && robustFrequency <= maxFrequency); - //System.out.println("freq: " + robustFrequency + " " + result); - return result; - } - - protected boolean isPassedIntensity(double[] spectrum){ - // get the average intensity of the signal - double intensity = 0; - for (int i = 0; i < spectrum.length; i++) { - intensity += spectrum[i]; - } - intensity /= spectrum.length; - // end get the average intensity of the signal - - // intensity of the whistle should not be too soft - boolean result = (intensity > minIntensity && intensity <= maxIntensity); - //System.out.println("intensity: " + intensity + " " + result); - - return result; - } - - protected boolean isPassedZeroCrossingRate(short[] amplitudes){ - ZeroCrossingRate zcr = new ZeroCrossingRate(amplitudes, 1); - int numZeroCrosses = (int) zcr.evaluate(); - - // different sound has different range of zero crossing value - // when lengthInSecond=1, zero crossing rate is the num - // of zero crosses - boolean result = (numZeroCrosses >= minNumZeroCross && numZeroCrosses <= maxNumZeroCross); - //System.out.println("zcr: " + numZeroCrosses + " " +result); - - return result; - } - -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/api/WhistleApi.java b/app/Locabean/src/com/musicg/api/WhistleApi.java deleted file mode 100644 index 3099f87..0000000 --- a/app/Locabean/src/com/musicg/api/WhistleApi.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.api; - -import com.musicg.wave.WaveHeader; - -/** - * Api for detecting whistle - * - * @author Jacquet Wong - * - */ -public class WhistleApi extends DetectionApi{ - - public WhistleApi(WaveHeader waveHeader) { - super(waveHeader); - } - - protected void init(){ - // settings for detecting a whistle - minFrequency = 600.0f; - maxFrequency = Double.MAX_VALUE; - - minIntensity = 100.0f; - maxIntensity = 100000.0f; - - minStandardDeviation = 0.1f; - maxStandardDeviation = 1.0f; - - highPass = 100; - lowPass = 10000; - - minNumZeroCross = 50; - maxNumZeroCross = 200; - - numRobust = 10; - } - - public boolean isWhistle(byte[] audioBytes){ - return isSpecificSound(audioBytes); - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/dsp/FastFourierTransform.java b/app/Locabean/src/com/musicg/dsp/FastFourierTransform.java deleted file mode 100644 index f8e27d8..0000000 --- a/app/Locabean/src/com/musicg/dsp/FastFourierTransform.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.dsp; - -import com.sun.media.sound.FFT; - -/** - * FFT object, transform amplitudes to frequency intensities - * - * @author Jacquet Wong - * - */ -public class FastFourierTransform { - - /** - * Get the frequency intensities - * - * @param amplitudes - * amplitudes of the signal - * @return intensities of each frequency unit: mag[frequency_unit]=intensity - */ - public double[] getMagnitudes(double[] amplitudes) { - - int sampleSize = amplitudes.length; - - // call the fft and transform the complex numbers - FFT fft = new FFT(sampleSize / 2, -1); - fft.transform(amplitudes); - // end call the fft and transform the complex numbers - - double[] complexNumbers = amplitudes; - - // even indexes (0,2,4,6,...) are real parts - // odd indexes (1,3,5,7,...) are img parts - int indexSize = sampleSize / 2; - - // FFT produces a transformed pair of arrays where the first half of the - // values represent positive frequency components and the second half - // represents negative frequency components. - // we omit the negative ones - int positiveSize = indexSize / 2; - - double[] mag = new double[positiveSize]; - for (int i = 0; i < indexSize; i += 2) { - mag[i / 2] = Math.sqrt(complexNumbers[i] * complexNumbers[i] + complexNumbers[i + 1] * complexNumbers[i + 1]); - } - - return mag; - } - -} diff --git a/app/Locabean/src/com/musicg/dsp/LinearInterpolation.java b/app/Locabean/src/com/musicg/dsp/LinearInterpolation.java deleted file mode 100644 index a74b433..0000000 --- a/app/Locabean/src/com/musicg/dsp/LinearInterpolation.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.dsp; - -/** - * Construct new data points within the range of a discrete set of known data points by linear equation - * - * @author Jacquet Wong - */ -public class LinearInterpolation { - - public LinearInterpolation(){ - - } - - /** - * Do interpolation on the samples according to the original and destinated sample rates - * - * @param oldSampleRate sample rate of the original samples - * @param newSampleRate sample rate of the interpolated samples - * @param samples original samples - * @return interpolated samples - */ - public short[] interpolate(int oldSampleRate, int newSampleRate, short[] samples) { - - if (oldSampleRate==newSampleRate){ - return samples; - } - - int newLength=(int)Math.round(((float)samples.length/oldSampleRate*newSampleRate)); - float lengthMultiplier=(float)newLength/samples.length; - short[] interpolatedSamples = new short[newLength]; - - // interpolate the value by the linear equation y=mx+c - for (int i = 0; i < newLength; i++){ - - // get the nearest positions for the interpolated point - float currentPosition = i / lengthMultiplier; - int nearestLeftPosition = (int)currentPosition; - int nearestRightPosition = nearestLeftPosition + 1; - if (nearestRightPosition>=samples.length){ - nearestRightPosition=samples.length-1; - } - - float slope=samples[nearestRightPosition]-samples[nearestLeftPosition]; // delta x is 1 - float positionFromLeft = currentPosition - nearestLeftPosition; - - interpolatedSamples[i] = (short)(slope*positionFromLeft+samples[nearestLeftPosition]); // y=mx+c - } - - return interpolatedSamples; - } -} diff --git a/app/Locabean/src/com/musicg/dsp/Resampler.java b/app/Locabean/src/com/musicg/dsp/Resampler.java deleted file mode 100644 index 1f0f517..0000000 --- a/app/Locabean/src/com/musicg/dsp/Resampler.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.dsp; - -/** - * Resample signal data (base on bytes) - * - * @author jacquet - * - */ -public class Resampler { - - public Resampler() { - } - - /** - * Do resampling. Currently the amplitude is stored by short such that maximum bitsPerSample is 16 (bytePerSample is 2) - * - * @param sourceData The source data in bytes - * @param bitsPerSample How many bits represents one sample (currently supports max. bitsPerSample=16) - * @param sourceRate Sample rate of the source data - * @param targetRate Sample rate of the target data - * @return re-sampled data - */ - public byte[] reSample(byte[] sourceData, int bitsPerSample, int sourceRate, int targetRate) { - - // make the bytes to amplitudes first - int bytePerSample = bitsPerSample / 8; - int numSamples = sourceData.length / bytePerSample; - short[] amplitudes = new short[numSamples]; // 16 bit, use a short to store - - int pointer = 0; - for (int i = 0; i < numSamples; i++) { - short amplitude = 0; - for (int byteNumber = 0; byteNumber < bytePerSample; byteNumber++) { - // little endian - amplitude |= (short) ((sourceData[pointer++] & 0xFF) << (byteNumber * 8)); - } - amplitudes[i] = amplitude; - } - // end make the amplitudes - - // do interpolation - LinearInterpolation reSample=new LinearInterpolation(); - short[] targetSample = reSample.interpolate(sourceRate, targetRate, amplitudes); - int targetLength = targetSample.length; - // end do interpolation - - // TODO: Remove the high frequency signals with a digital filter, leaving a signal containing only half-sample-rated frequency information, but still sampled at a rate of target sample rate. Usually FIR is used - - // end resample the amplitudes - - // convert the amplitude to bytes - byte[] bytes; - if (bytePerSample==1){ - bytes= new byte[targetLength]; - for (int i=0; i> 8) & 0xff); - } - } - // end convert the amplitude to bytes - - return bytes; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/dsp/WindowFunction.java b/app/Locabean/src/com/musicg/dsp/WindowFunction.java deleted file mode 100644 index 3043565..0000000 --- a/app/Locabean/src/com/musicg/dsp/WindowFunction.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.dsp; - -/** - * Window functions generator - * - * @author Jacquet Wong - * - */ -public class WindowFunction { - - public static final int RECTANGULAR = 0; - public static final int BARTLETT = 1; - public static final int HANNING = 2; - public static final int HAMMING = 3; - public static final int BLACKMAN = 4; - - int windowType = 0; // defaults to rectangular window - - public WindowFunction() { - } - - public void setWindowType(int wt) { - windowType = wt; - } - - public void setWindowType(String w) { - if (w.toUpperCase().equals("RECTANGULAR")) - windowType = RECTANGULAR; - if (w.toUpperCase().equals("BARTLETT")) - windowType = BARTLETT; - if (w.toUpperCase().equals("HANNING")) - windowType = HANNING; - if (w.toUpperCase().equals("HAMMING")) - windowType = HAMMING; - if (w.toUpperCase().equals("BLACKMAN")) - windowType = BLACKMAN; - } - - public int getWindowType() { - return windowType; - } - - /** - * Generate a window - * - * @param nSamples size of the window - * @return window in array - */ - public double[] generate(int nSamples) { - // generate nSamples window function values - // for index values 0 .. nSamples - 1 - int m = nSamples / 2; - double r; - double pi = Math.PI; - double[] w = new double[nSamples]; - switch (windowType) { - case BARTLETT: // Bartlett (triangular) window - for (int n = 0; n < nSamples; n++) - w[n] = 1.0f - Math.abs(n - m) / m; - break; - case HANNING: // Hanning window - r = pi / (m + 1); - for (int n = -m; n < m; n++) - w[m + n] = 0.5f + 0.5f * Math.cos(n * r); - break; - case HAMMING: // Hamming window - r = pi / m; - for (int n = -m; n < m; n++) - w[m + n] = 0.54f + 0.46f * Math.cos(n * r); - break; - case BLACKMAN: // Blackman window - r = pi / m; - for (int n = -m; n < m; n++) - w[m + n] = 0.42f + 0.5f * Math.cos(n * r) + 0.08f - * Math.cos(2 * n * r); - break; - default: // Rectangular window function - for (int n = 0; n < nSamples; n++) - w[n] = 1.0f; - } - return w; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/experiment/math/cluster/Segment.java b/app/Locabean/src/com/musicg/experiment/math/cluster/Segment.java deleted file mode 100644 index f3013fa..0000000 --- a/app/Locabean/src/com/musicg/experiment/math/cluster/Segment.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.experiment.math.cluster; -public class Segment { - - private int startPosition; - private int size; - private double mean; - - public int getStartPosition() { - return startPosition; - } - - public void setStartPosition(int startPosition) { - this.startPosition = startPosition; - } - - public int getSize() { - return size; - } - - public void setSize(int size) { - this.size = size; - } - - public double getMean() { - return mean; - } - - public void setMean(double mean) { - this.mean = mean; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/experiment/math/cluster/SegmentCluster.java b/app/Locabean/src/com/musicg/experiment/math/cluster/SegmentCluster.java deleted file mode 100644 index 1dde27d..0000000 --- a/app/Locabean/src/com/musicg/experiment/math/cluster/SegmentCluster.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.experiment.math.cluster; -import java.util.LinkedList; -import java.util.List; - -import com.musicg.pitch.PitchHandler; - -public class SegmentCluster{ - - private double diffThreshold; - - public SegmentCluster(){ - this.diffThreshold=1; - } - - public SegmentCluster(double diffThreshold){ - this.diffThreshold=diffThreshold; - } - - public void setDiffThreshold(double diffThreshold){ - this.diffThreshold=diffThreshold; - } - - public List getSegments(double[] array){ - - PitchHandler pitchHandler=new PitchHandler(); - List segmentList=new LinkedList(); - - double segmentMean=0; - int segmentSize=0; - - if (array.length>0){ - segmentMean=array[1]; - segmentSize=1; - } - - for (int i=1; i[] 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 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 byteList = new LinkedList(); - 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 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[] getRobustPointList(double[][] spectrogramData) { - - System.out.println(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 robustPointList = new LinkedList(); - - // 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[] robustLists = new LinkedList[spectrogramData.length]; - for (int i = 0; i < robustLists.length; i++) { - robustLists[i] = new LinkedList(); - } - - // robustLists[x]=y1,y2,y3,... - Iterator 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; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/fingerprint/FingerprintSimilarity.java b/app/Locabean/src/com/musicg/fingerprint/FingerprintSimilarity.java deleted file mode 100644 index 0330fdc..0000000 --- a/app/Locabean/src/com/musicg/fingerprint/FingerprintSimilarity.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 com.musicg.properties.FingerprintProperties; - -/** - * A class for fingerprint's similarity - * - * @author jacquet - * - */ -public class FingerprintSimilarity { - - private FingerprintProperties fingerprintProperties=FingerprintProperties.getInstance(); - private int mostSimilarFramePosition; - private float score; - private float similarity; - - /** - * Constructor - */ - public FingerprintSimilarity() { - mostSimilarFramePosition = Integer.MIN_VALUE; - score=-1; - similarity = -1; - } - - /** - * Get the most similar position in terms of frame number - * - * @return most similar frame position - */ - public int getMostSimilarFramePosition() { - return mostSimilarFramePosition; - } - - /** - * Set the most similar position in terms of frame number - * - * @param mostSimilarFramePosition - */ - public void setMostSimilarFramePosition(int mostSimilarFramePosition) { - this.mostSimilarFramePosition = mostSimilarFramePosition; - } - - /** - * Get the similarity of the fingerprints - * similarity from 0~1, which 0 means no similar feature is found and 1 means in average there is at least one match in every frame - * - * @return fingerprints similarity - */ - public float getSimilarity() { - return similarity; - } - - /** - * Set the similarity of the fingerprints - * - * @param fingerprints similarity - */ - public void setSimilarity(float similarity) { - this.similarity = similarity; - } - - /** - * Get the similarity score of the fingerprints - * Number of features found in the fingerprints per frame - * - * @return fingerprints similarity score - */ - public float getScore() { - return score; - } - - /** - * Set the similarity score of the fingerprints - * - * @param score - */ - public void setScore(float score) { - this.score = score; - } - - /** - * Get the most similar position in terms of time in second - * - * @return most similar starting time - */ - public float getsetMostSimilarTimePosition(){ - return (float)mostSimilarFramePosition/fingerprintProperties.getNumRobustPointsPerFrame()/fingerprintProperties.getFps(); - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/fingerprint/FingerprintSimilarityComputer.java b/app/Locabean/src/com/musicg/fingerprint/FingerprintSimilarityComputer.java deleted file mode 100644 index ee16148..0000000 --- a/app/Locabean/src/com/musicg/fingerprint/FingerprintSimilarityComputer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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.util.HashMap; -import java.util.Iterator; -import java.util.List; - -import com.musicg.math.rank.MapRank; -import com.musicg.math.rank.MapRankInteger; - -/** - * Compute the similarity of two fingerprints - * - * @author jacquet - * - */ -public class FingerprintSimilarityComputer{ - - private FingerprintSimilarity fingerprintSimilarity; - byte[] fingerprint1, fingerprint2; - - /** - * Constructor, ready to compute the similarity of two fingerprints - * - * @param fingerprint1 - * @param fingerprint2 - */ - public FingerprintSimilarityComputer(byte[] fingerprint1, byte[] fingerprint2){ - - this.fingerprint1=fingerprint1; - this.fingerprint2=fingerprint2; - - fingerprintSimilarity=new FingerprintSimilarity(); - } - - /** - * Get fingerprint similarity of inout fingerprints - * - * @return fingerprint similarity object - */ - public FingerprintSimilarity getFingerprintsSimilarity(){ - HashMap offset_Score_Table=new HashMap(); // offset_Score_Table - int numFrames=0; - float score=0; - int mostSimilarFramePosition=Integer.MIN_VALUE; - - // one frame may contain several points, use the shorter one be the denominator - if (fingerprint1.length>fingerprint2.length){ - numFrames=FingerprintManager.getNumFrames(fingerprint2); - } - else{ - numFrames=FingerprintManager.getNumFrames(fingerprint1); - } - - // get the pairs - PairManager pairManager=new PairManager(); - HashMap> this_Pair_PositionList_Table=pairManager.getPair_PositionList_Table(fingerprint1); - HashMap> compareWave_Pair_PositionList_Table=pairManager.getPair_PositionList_Table(fingerprint2); - - Iterator compareWaveHashNumberIterator=compareWave_Pair_PositionList_Table.keySet().iterator(); - while (compareWaveHashNumberIterator.hasNext()){ - int compareWaveHashNumber=compareWaveHashNumberIterator.next(); - - // if the compareWaveHashNumber doesn't exist in both tables, no need to compare - if (!this_Pair_PositionList_Table.containsKey(compareWaveHashNumber) - || !compareWave_Pair_PositionList_Table.containsKey(compareWaveHashNumber)){ - continue; - } - - // for each compare hash number, get the positions - List wavePositionList=this_Pair_PositionList_Table.get(compareWaveHashNumber); - List compareWavePositionList=compareWave_Pair_PositionList_Table.get(compareWaveHashNumber); - - Iterator wavePositionListIterator=wavePositionList.iterator(); - while (wavePositionListIterator.hasNext()){ - int thisPosition=wavePositionListIterator.next(); - Iterator compareWavePositionListIterator=compareWavePositionList.iterator(); - while (compareWavePositionListIterator.hasNext()){ - int compareWavePosition=compareWavePositionListIterator.next(); - int offset=thisPosition-compareWavePosition; - - if (offset_Score_Table.containsKey(offset)){ - offset_Score_Table.put(offset, offset_Score_Table.get(offset)+1); - } - else{ - offset_Score_Table.put(offset, 1); - } - } - } - } - - // map rank - MapRank mapRank=new MapRankInteger(offset_Score_Table,false); - - // get the most similar positions and scores - List orderedKeyList=mapRank.getOrderedKeyList(100, true); - if (orderedKeyList.size()>0){ - int key=orderedKeyList.get(0); - // get the highest score position - if (mostSimilarFramePosition==Integer.MIN_VALUE){ - mostSimilarFramePosition=key; - score=offset_Score_Table.get(key); - - // accumulate the scores from neighbours - if (offset_Score_Table.containsKey(key-1)){ - score+=offset_Score_Table.get(key-1)/2; - } - if (offset_Score_Table.containsKey(key+1)){ - score+=offset_Score_Table.get(key+1)/2; - } - } - } - - /* - Iterator orderedKeyListIterator=orderedKeyList.iterator(); - while (orderedKeyListIterator.hasNext()){ - int offset=orderedKeyListIterator.next(); - System.out.println(offset+": "+offset_Score_Table.get(offset)); - } - */ - - score/=numFrames; - float similarity=score; - // similarity >1 means in average there is at least one match in every frame - if (similarity>1){ - similarity=1; - } - - fingerprintSimilarity.setMostSimilarFramePosition(mostSimilarFramePosition); - fingerprintSimilarity.setScore(score); - fingerprintSimilarity.setSimilarity(similarity); - - return fingerprintSimilarity; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/fingerprint/PairManager.java b/app/Locabean/src/com/musicg/fingerprint/PairManager.java deleted file mode 100644 index 1e86d2f..0000000 --- a/app/Locabean/src/com/musicg/fingerprint/PairManager.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import com.musicg.math.quicksort.QuickSortIndexPreserved; -import com.musicg.properties.FingerprintProperties; - -/** - * Make pairs for the audio fingerprints, which a pair is used to group the same features together - * - * @author jacquet - * - */ -public class PairManager{ - - FingerprintProperties fingerprintProperties=FingerprintProperties.getInstance(); - private int numFilterBanks=fingerprintProperties.getNumFilterBanks(); - private int bandwidthPerBank=fingerprintProperties.getNumFrequencyUnits()/numFilterBanks; - private int anchorPointsIntervalLength=fingerprintProperties.getAnchorPointsIntervalLength(); - private int numAnchorPointsPerInterval=fingerprintProperties.getNumAnchorPointsPerInterval(); - private int maxTargetZoneDistance=fingerprintProperties.getMaxTargetZoneDistance(); - private int numFrequencyUnits=fingerprintProperties.getNumFrequencyUnits(); - - private int maxPairs; - private boolean isReferencePairing; - private HashMap stopPairTable=new HashMap(); - - /** - * Constructor - */ - public PairManager(){ - maxPairs=fingerprintProperties.getRefMaxActivePairs(); - isReferencePairing=true; - } - - /** - * Constructor, number of pairs of robust points depends on the parameter isReferencePairing - * no. of pairs of reference and sample can be different due to environmental influence of source - * @param isReferencePairing - */ - public PairManager(boolean isReferencePairing){ - if (isReferencePairing){ - maxPairs=fingerprintProperties.getRefMaxActivePairs(); - } - else{ - maxPairs=fingerprintProperties.getSampleMaxActivePairs(); - } - this.isReferencePairing=isReferencePairing; - } - - /** - * Get a pair-positionList table - * It's a hash map which the key is the hashed pair, and the value is list of positions - * That means the table stores the positions which have the same hashed pair - * - * @param fingerprint fingerprint bytes - * @return pair-positionList HashMap - */ - public HashMap> getPair_PositionList_Table(byte[] fingerprint){ - - List pairPositionList=getPairPositionList(fingerprint); - - // table to store pair:pos,pos,pos,...;pair2:pos,pos,pos,.... - HashMap> pair_positionList_table=new HashMap>(); - - // get all pair_positions from list, use a table to collect the data group by pair hashcode - Iterator pairPositionListIterator=pairPositionList.iterator(); - while (pairPositionListIterator.hasNext()){ - int[] pair_position=pairPositionListIterator.next(); - //System.out.println(pair_position[0]+","+pair_position[1]); - - // group by pair-hashcode, i.e.: > - if (pair_positionList_table.containsKey(pair_position[0])){ - pair_positionList_table.get(pair_position[0]).add(pair_position[1]); - } - else{ - List positionList=new LinkedList(); - positionList.add(pair_position[1]); - pair_positionList_table.put(pair_position[0], positionList); - } - // end group by pair-hashcode, i.e.: > - } - // end get all pair_positions from list, use a table to collect the data group by pair hashcode - - return pair_positionList_table; - } - - // this return list contains: int[0]=pair_hashcode, int[1]=position - private List getPairPositionList(byte[] fingerprint){ - - int numFrames=FingerprintManager.getNumFrames(fingerprint); - - // table for paired frames - byte[] pairedFrameTable=new byte[numFrames/anchorPointsIntervalLength+1]; // each second has numAnchorPointsPerSecond pairs only - // end table for paired frames - - List pairList=new LinkedList(); - List sortedCoordinateList=getSortedCoordinateList(fingerprint); - - Iterator anchorPointListIterator=sortedCoordinateList.iterator(); - while (anchorPointListIterator.hasNext()){ - int[] anchorPoint=anchorPointListIterator.next(); - int anchorX=anchorPoint[0]; - int anchorY=anchorPoint[1]; - int numPairs=0; - - Iterator targetPointListIterator=sortedCoordinateList.iterator(); - while (targetPointListIterator.hasNext()){ - - if (numPairs>=maxPairs){ - break; - } - - if (isReferencePairing && pairedFrameTable[anchorX/anchorPointsIntervalLength]>=numAnchorPointsPerInterval){ - break; - } - - int[] targetPoint=targetPointListIterator.next(); - int targetX=targetPoint[0]; - int targetY=targetPoint[1]; - - if (anchorX==targetX && anchorY==targetY){ - continue; - } - - // pair up the points - int x1,y1,x2,y2; // x2 always >= x1 - if (targetX>=anchorX){ - x2=targetX; - y2=targetY; - x1=anchorX; - y1=anchorY; - } - else{ - x2=anchorX; - y2=anchorY; - x1=targetX; - y1=targetY; - } - - // check target zone - if ((x2-x1)>maxTargetZoneDistance){ - continue; - } - // end check target zone - - // check filter bank zone - if (!(y1/bandwidthPerBank == y2/bandwidthPerBank)){ - continue; // same filter bank should have equal value - } - // end check filter bank zone - - int pairHashcode=(x2-x1)*numFrequencyUnits*numFrequencyUnits+y2*numFrequencyUnits+y1; - - // stop list applied on sample pairing only - if (!isReferencePairing && stopPairTable.containsKey(pairHashcode)){ - numPairs++; // no reservation - continue; // escape this point only - } - // end stop list applied on sample pairing only - - // pass all rules - pairList.add(new int[]{pairHashcode,anchorX}); - pairedFrameTable[anchorX/anchorPointsIntervalLength]++; - //System.out.println(anchorX+","+anchorY+"&"+targetX+","+targetY+":"+pairHashcode+" ("+pairedFrameTable[anchorX/anchorPointsIntervalLength]+")"); - numPairs++; - // end pair up the points - } - } - - return pairList; - } - - private List getSortedCoordinateList(byte[] fingerprint){ - // each point data is 8 bytes - // first 2 bytes is x - // next 2 bytes is y - // next 4 bytes is intensity - - // get all intensities - int numCoordinates=fingerprint.length/8; - int[] intensities=new int[numCoordinates]; - for (int i=0; i sortedCoordinateList=new LinkedList(); - for (int i=sortIndexes.length-1; i>=0; i--){ - int pointer=sortIndexes[i]*8; - int x=(int)(fingerprint[pointer]&0xff)<<8 | (int)(fingerprint[pointer+1]&0xff); - int y=(int)(fingerprint[pointer+2]&0xff)<<8 | (int)(fingerprint[pointer+3]&0xff); - sortedCoordinateList.add(new int[]{x,y}); - } - return sortedCoordinateList; - } - - /** - * Convert hashed pair to bytes - * - * @param pairHashcode hashed pair - * @return byte array - */ - public static byte[] pairHashcodeToBytes(int pairHashcode){ - return new byte[]{(byte)(pairHashcode>>8),(byte)pairHashcode}; - } - - /** - * Convert bytes to hased pair - * - * @param pairBytes - * @return hashed pair - */ - public static int pairBytesToHashcode(byte[] pairBytes){ - return (int)(pairBytes[0]&0xFF)<<8|(int)(pairBytes[1]&0xFF); - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/main/demo/FingerprintDemo.java b/app/Locabean/src/com/musicg/main/demo/FingerprintDemo.java deleted file mode 100644 index 31a4074..0000000 --- a/app/Locabean/src/com/musicg/main/demo/FingerprintDemo.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.main.demo; - -import com.musicg.fingerprint.FingerprintManager; -import com.musicg.wave.Wave; -import java.util.Arrays; - -public class FingerprintDemo{ - - public static void main (String[] args){ - - String filename = "cock_a_1.wav"; - - // create a wave object - Wave wave = new Wave("audio_work/"+filename); - - // get the fingerprint - byte[] fingerprint=wave.getFingerprint(); - System.out.println(Arrays.toString(fingerprint)); - // dump the fingerprint - FingerprintManager fingerprintManager=new FingerprintManager(); - fingerprintManager.saveFingerprintAsFile(fingerprint, "out/"+filename+".fingerprint"); - - // load fingerprint from file - byte[] loadedFp=fingerprintManager.getFingerprintFromFile("out/"+filename+".fingerprint"); - - // fingerprint bytes checking -// for (int i=0; i0){ - System.out.println(i+": "+processedIntensities[i][j]); - } - } - } - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/main/demo/WaveDemo.java b/app/Locabean/src/com/musicg/main/demo/WaveDemo.java deleted file mode 100644 index 707045a..0000000 --- a/app/Locabean/src/com/musicg/main/demo/WaveDemo.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.main.demo; - -import com.musicg.wave.Wave; -import com.musicg.wave.WaveFileManager; - -public class WaveDemo { - - public static void main(String[] args) { - - String filename = "audio_work/cock_a_1.wav"; - String outFolder="out"; - - // create a wave object - Wave wave = new Wave(filename); - - // print the wave header and info - System.out.println(wave); - - // trim the wav - wave.leftTrim(1); - wave.rightTrim(0.5F); - - // save the trimmed wav - WaveFileManager waveFileManager=new WaveFileManager(wave); - waveFileManager.saveWaveAsFile(outFolder+"/out.wav"); - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/main/demo/WhistleApiDemo.java b/app/Locabean/src/com/musicg/main/demo/WhistleApiDemo.java deleted file mode 100644 index e1eb4bd..0000000 --- a/app/Locabean/src/com/musicg/main/demo/WhistleApiDemo.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.main.demo; - -import com.musicg.wave.Wave; -import com.musicg.wave.WaveTypeDetector; - -public class WhistleApiDemo{ - public static void main(String[] args){ - String filename = "audio_work/whistle.wav"; - - // create a wave object - Wave wave = new Wave(filename); - - WaveTypeDetector waveTypeDetector=new WaveTypeDetector(wave); - System.out.println("Is whistle probability: "+waveTypeDetector.getWhistleProbability()); - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/quicksort/QuickSort.java b/app/Locabean/src/com/musicg/math/quicksort/QuickSort.java deleted file mode 100644 index 5b3acb4..0000000 --- a/app/Locabean/src/com/musicg/math/quicksort/QuickSort.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.musicg.math.quicksort; - -public abstract class QuickSort{ - public abstract int[] getSortIndexes(); -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/quicksort/QuickSortDouble.java b/app/Locabean/src/com/musicg/math/quicksort/QuickSortDouble.java deleted file mode 100644 index fbc9e59..0000000 --- a/app/Locabean/src/com/musicg/math/quicksort/QuickSortDouble.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.musicg.math.quicksort; -public class QuickSortDouble extends QuickSort{ - - private int[] indexes; - private double[] array; - - public QuickSortDouble(double[] array){ - this.array=array; - indexes=new int[array.length]; - for (int i=0; i= j) break; // check if pointers cross - swap(a, indexes, i, j); // swap two elements into place - } - swap(a, indexes, i, right); // swap with partition element - return i; - } - - // exchange a[i] and a[j] - private void swap(double[] a, int[] indexes, int i, int j) { - int swap = indexes[i]; - indexes[i] = indexes[j]; - indexes[j] = swap; - } - -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/quicksort/QuickSortIndexPreserved.java b/app/Locabean/src/com/musicg/math/quicksort/QuickSortIndexPreserved.java deleted file mode 100644 index 5a333e4..0000000 --- a/app/Locabean/src/com/musicg/math/quicksort/QuickSortIndexPreserved.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.musicg.math.quicksort; -public class QuickSortIndexPreserved { - - private QuickSort quickSort; - - public QuickSortIndexPreserved(int[] array){ - quickSort=new QuickSortInteger(array); - } - - public QuickSortIndexPreserved(double[] array){ - quickSort=new QuickSortDouble(array); - } - - public QuickSortIndexPreserved(short[] array){ - quickSort=new QuickSortShort(array); - } - - public int[] getSortIndexes(){ - return quickSort.getSortIndexes(); - } - -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/quicksort/QuickSortInteger.java b/app/Locabean/src/com/musicg/math/quicksort/QuickSortInteger.java deleted file mode 100644 index a6684c2..0000000 --- a/app/Locabean/src/com/musicg/math/quicksort/QuickSortInteger.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.musicg.math.quicksort; -public class QuickSortInteger extends QuickSort{ - - private int[] indexes; - private int[] array; - - public QuickSortInteger(int[] array){ - this.array=array; - indexes=new int[array.length]; - for (int i=0; i= j) break; // check if pointers cross - swap(a, indexes, i, j); // swap two elements into place - } - swap(a, indexes, i, right); // swap with partition element - return i; - } - - // exchange a[i] and a[j] - private void swap(int[] a, int[] indexes, int i, int j) { - int swap = indexes[i]; - indexes[i] = indexes[j]; - indexes[j] = swap; - } - -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/quicksort/QuickSortShort.java b/app/Locabean/src/com/musicg/math/quicksort/QuickSortShort.java deleted file mode 100644 index 271407b..0000000 --- a/app/Locabean/src/com/musicg/math/quicksort/QuickSortShort.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.musicg.math.quicksort; -public class QuickSortShort extends QuickSort{ - - private int[] indexes; - private short[] array; - - public QuickSortShort(short[] array){ - this.array=array; - indexes=new int[array.length]; - for (int i=0; i= j) break; // check if pointers cross - swap(a, indexes, i, j); // swap two elements into place - } - swap(a, indexes, i, right); // swap with partition element - return i; - } - - // exchange a[i] and a[j] - private void swap(short[] a, int[] indexes, int i, int j) { - int swap = indexes[i]; - indexes[i] = indexes[j]; - indexes[j] = swap; - } - -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/rank/ArrayRankDouble.java b/app/Locabean/src/com/musicg/math/rank/ArrayRankDouble.java deleted file mode 100644 index 1881680..0000000 --- a/app/Locabean/src/com/musicg/math/rank/ArrayRankDouble.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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.math.rank; - -public class ArrayRankDouble { - - /** - * Get the index position of maximum value the given array - * @param array - * @return index of the max value in array - */ - public int getMaxValueIndex(double[] array) { - - int index = 0; - double max = Integer.MIN_VALUE; - - for (int i = 0; i < array.length; i++) { - if (array[i] > max) { - max = array[i]; - index = i; - } - } - - return index; - } - - /** - * Get the index position of minimum value in the given array - * @param array - * @return index of the min value in array - */ - public int getMinValueIndex(double[] array) { - - int index = 0; - double min = Integer.MAX_VALUE; - - for (int i = 0; i < array.length; i++) { - if (array[i] < min) { - min = array[i]; - index = i; - } - } - - return index; - } - - /** - * Get the n-th value in the array after sorted - * @param array - * @param n - * @param ascending is ascending order or not - * @return - */ - public double getNthOrderedValue(double[] array, int n, boolean ascending) { - - if (n > array.length) { - n = array.length; - } - - int targetindex; - if (ascending) { - targetindex = n; - } else { - targetindex = array.length - n; - } - - // this value is the value of the numKey-th element - double passValue = getOrderedValue(array, targetindex); - - return passValue; - } - - private double getOrderedValue(double[] array, int index) { - locate(array, 0, array.length - 1, index); - return array[index]; - } - - // sort the partitions by quick sort, and locate the target index - private void locate(double[] array, int left, int right, int index) { - - int mid = (left + right) / 2; - // System.out.println(left+" to "+right+" ("+mid+")"); - - if (right == left) { - // System.out.println("* "+array[targetIndex]); - // result=array[targetIndex]; - return; - } - - if (left < right) { - double s = array[mid]; - int i = left - 1; - int j = right + 1; - - while (true) { - while (array[++i] < s) - ; - while (array[--j] > s) - ; - if (i >= j) - break; - swap(array, i, j); - } - - // System.out.println("2 parts: "+left+"-"+(i-1)+" and "+(j+1)+"-"+right); - - if (i > index) { - // the target index in the left partition - // System.out.println("left partition"); - locate(array, left, i - 1, index); - } else { - // the target index in the right partition - // System.out.println("right partition"); - locate(array, j + 1, right, index); - } - } - } - - private void swap(double[] array, int i, int j) { - double t = array[i]; - array[i] = array[j]; - array[j] = t; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/rank/MapRank.java b/app/Locabean/src/com/musicg/math/rank/MapRank.java deleted file mode 100644 index 24c4e49..0000000 --- a/app/Locabean/src/com/musicg/math/rank/MapRank.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.musicg.math.rank; - -import java.util.List; - -public interface MapRank{ - public List getOrderedKeyList(int numKeys, boolean sharpLimit); -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/rank/MapRankDouble.java b/app/Locabean/src/com/musicg/math/rank/MapRankDouble.java deleted file mode 100644 index 160a198..0000000 --- a/app/Locabean/src/com/musicg/math/rank/MapRankDouble.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.musicg.math.rank; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -public class MapRankDouble implements MapRank{ - - private Map map; - private boolean acsending=true; - - public MapRankDouble(Map map, boolean acsending){ - this.map=map; - this.acsending=acsending; - } - - public List getOrderedKeyList(int numKeys, boolean sharpLimit){ // if sharp limited, will return sharp numKeys, otherwise will return until the values not equals the exact key's value - - Set mapEntrySet=map.entrySet(); - List keyList=new LinkedList(); - - // if the numKeys is larger than map size, limit it - if (numKeys>map.size()){ - numKeys=map.size(); - } - // end if the numKeys is larger than map size, limit it - - if (map.size()>0){ - double[] array=new double[map.size()]; - int count=0; - - // get the pass values - Iterator mapIterator=mapEntrySet.iterator(); - while (mapIterator.hasNext()){ - Entry entry=mapIterator.next(); - array[count++]=(Double)entry.getValue(); - } - // end get the pass values - - int targetindex; - if (acsending){ - targetindex=numKeys; - } - else{ - targetindex=array.length-numKeys; - } - - double passValue=getOrderedValue(array,targetindex); // this value is the value of the numKey-th element - // get the passed keys and values - Map passedMap=new HashMap(); - List valueList=new LinkedList(); - mapIterator=mapEntrySet.iterator(); - - while (mapIterator.hasNext()){ - Entry entry=mapIterator.next(); - double value=(Double)entry.getValue(); - if ((acsending && value<=passValue) || (!acsending && value>=passValue)){ - passedMap.put(entry.getKey(), value); - valueList.add(value); - } - } - // end get the passed keys and values - - // sort the value list - Double[] listArr=new Double[valueList.size()]; - valueList.toArray(listArr); - Arrays.sort(listArr); - // end sort the value list - - // get the list of keys - int resultCount=0; - int index; - if (acsending){ - index=0; - } - else{ - index=listArr.length-1; - } - - if (!sharpLimit){ - numKeys=listArr.length; - } - - while (true){ - double targetValue=(Double)listArr[index]; - Iterator passedMapIterator=passedMap.entrySet().iterator(); - while(passedMapIterator.hasNext()){ - Entry entry=passedMapIterator.next(); - if ((Double)entry.getValue()==targetValue){ - keyList.add(entry.getKey()); - passedMapIterator.remove(); - resultCount++; - break; - } - } - - if (acsending){ - index++; - } - else{ - index--; - } - - if (resultCount>=numKeys){ - break; - } - } - // end get the list of keys - } - - return keyList; - } - - private double getOrderedValue(double[] array, int index){ - locate(array,0,array.length-1,index); - return array[index]; - } - - // sort the partitions by quick sort, and locate the target index - private void locate(double[] array, int left, int right, int index) { - - int mid=(left+right)/2; - //System.out.println(left+" to "+right+" ("+mid+")"); - - if (right==left){ - //System.out.println("* "+array[targetIndex]); - //result=array[targetIndex]; - return; - } - - if(left < right) { - double s = array[mid]; - int i = left - 1; - int j = right + 1; - - while(true) { - while(array[++i] < s) ; - while(array[--j] > s) ; - if(i >= j) - break; - swap(array, i, j); - } - - //System.out.println("2 parts: "+left+"-"+(i-1)+" and "+(j+1)+"-"+right); - - if (i>index){ - // the target index in the left partition - //System.out.println("left partition"); - locate(array, left, i-1,index); - } - else{ - // the target index in the right partition - //System.out.println("right partition"); - locate(array, j+1, right,index); - } - } - } - - private void swap(double[] array, int i, int j) { - double t = array[i]; - array[i] = array[j]; - array[j] = t; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/rank/MapRankInteger.java b/app/Locabean/src/com/musicg/math/rank/MapRankInteger.java deleted file mode 100644 index b7c528b..0000000 --- a/app/Locabean/src/com/musicg/math/rank/MapRankInteger.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.musicg.math.rank; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -public class MapRankInteger implements MapRank{ - - private Map map; - private boolean acsending=true; - - public MapRankInteger(Map map, boolean acsending){ - this.map=map; - this.acsending=acsending; - } - - public List getOrderedKeyList(int numKeys, boolean sharpLimit){ // if sharp limited, will return sharp numKeys, otherwise will return until the values not equals the exact key's value - - Set mapEntrySet=map.entrySet(); - List keyList=new LinkedList(); - - // if the numKeys is larger than map size, limit it - if (numKeys>map.size()){ - numKeys=map.size(); - } - // end if the numKeys is larger than map size, limit it - - if (map.size()>0){ - int[] array=new int[map.size()]; - int count=0; - - // get the pass values - Iterator mapIterator=mapEntrySet.iterator(); - while (mapIterator.hasNext()){ - Entry entry=mapIterator.next(); - array[count++]=(Integer)entry.getValue(); - } - // end get the pass values - - int targetindex; - if (acsending){ - targetindex=numKeys; - } - else{ - targetindex=array.length-numKeys; - } - - int passValue=getOrderedValue(array,targetindex); // this value is the value of the numKey-th element - // get the passed keys and values - Map passedMap=new HashMap(); - List valueList=new LinkedList(); - mapIterator=mapEntrySet.iterator(); - - while (mapIterator.hasNext()){ - Entry entry=mapIterator.next(); - int value=(Integer)entry.getValue(); - if ((acsending && value<=passValue) || (!acsending && value>=passValue)){ - passedMap.put(entry.getKey(), value); - valueList.add(value); - } - } - // end get the passed keys and values - - // sort the value list - Integer[] listArr=new Integer[valueList.size()]; - valueList.toArray(listArr); - Arrays.sort(listArr); - // end sort the value list - - // get the list of keys - int resultCount=0; - int index; - if (acsending){ - index=0; - } - else{ - index=listArr.length-1; - } - - if (!sharpLimit){ - numKeys=listArr.length; - } - - while (true){ - int targetValue=(Integer)listArr[index]; - Iterator passedMapIterator=passedMap.entrySet().iterator(); - while(passedMapIterator.hasNext()){ - Entry entry=passedMapIterator.next(); - if ((Integer)entry.getValue()==targetValue){ - keyList.add(entry.getKey()); - passedMapIterator.remove(); - resultCount++; - break; - } - } - - if (acsending){ - index++; - } - else{ - index--; - } - - if (resultCount>=numKeys){ - break; - } - } - // end get the list of keys - } - - return keyList; - } - - private int getOrderedValue(int[] array, int index){ - locate(array,0,array.length-1,index); - return array[index]; - } - - // sort the partitions by quick sort, and locate the target index - private void locate(int[] array, int left, int right, int index) { - - int mid=(left+right)/2; - //System.out.println(left+" to "+right+" ("+mid+")"); - - if (right==left){ - //System.out.println("* "+array[targetIndex]); - //result=array[targetIndex]; - return; - } - - if(left < right) { - int s = array[mid]; - int i = left - 1; - int j = right + 1; - - while(true) { - while(array[++i] < s) ; - while(array[--j] > s) ; - if(i >= j) - break; - swap(array, i, j); - } - - //System.out.println("2 parts: "+left+"-"+(i-1)+" and "+(j+1)+"-"+right); - - if (i>index){ - // the target index in the left partition - //System.out.println("left partition"); - locate(array, left, i-1,index); - } - else{ - // the target index in the right partition - //System.out.println("right partition"); - locate(array, j+1, right,index); - } - } - } - - private void swap(int[] array, int i, int j) { - int t = array[i]; - array[i] = array[j]; - array[j] = t; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/rank/MapRankShort.java b/app/Locabean/src/com/musicg/math/rank/MapRankShort.java deleted file mode 100644 index 89e6ca2..0000000 --- a/app/Locabean/src/com/musicg/math/rank/MapRankShort.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.musicg.math.rank; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -public class MapRankShort implements MapRank{ - - private Map map; - private boolean acsending=true; - - public MapRankShort(Map map, boolean acsending){ - this.map=map; - this.acsending=acsending; - } - - public List getOrderedKeyList(int numKeys, boolean sharpLimit){ // if sharp limited, will return sharp numKeys, otherwise will return until the values not equals the exact key's value - - Set mapEntrySet=map.entrySet(); - List keyList=new LinkedList(); - - // if the numKeys is larger than map size, limit it - if (numKeys>map.size()){ - numKeys=map.size(); - } - // end if the numKeys is larger than map size, limit it - - if (map.size()>0){ - - short[] array=new short[map.size()]; - int count=0; - - // get the pass values - Iterator mapIterator=mapEntrySet.iterator(); - while (mapIterator.hasNext()){ - Entry entry=mapIterator.next(); - array[count++]=(Short)entry.getValue(); - } - // end get the pass values - - int targetindex; - if (acsending){ - targetindex=numKeys; - } - else{ - targetindex=array.length-numKeys; - } - - short passValue=getOrderedValue(array,targetindex); // this value is the value of the numKey-th element - // get the passed keys and values - Map passedMap=new HashMap(); - List valueList=new LinkedList(); - mapIterator=mapEntrySet.iterator(); - - while (mapIterator.hasNext()){ - Entry entry=mapIterator.next(); - short value=(Short)entry.getValue(); - if ((acsending && value<=passValue) || (!acsending && value>=passValue)){ - passedMap.put(entry.getKey(), value); - valueList.add(value); - } - } - // end get the passed keys and values - - // sort the value list - Short[] listArr=new Short[valueList.size()]; - valueList.toArray(listArr); - Arrays.sort(listArr); - // end sort the value list - - // get the list of keys - int resultCount=0; - int index; - if (acsending){ - index=0; - } - else{ - index=listArr.length-1; - } - - if (!sharpLimit){ - numKeys=listArr.length; - } - - while (true){ - short targetValue=(Short)listArr[index]; - Iterator passedMapIterator=passedMap.entrySet().iterator(); - while(passedMapIterator.hasNext()){ - Entry entry=passedMapIterator.next(); - if ((Short)entry.getValue()==targetValue){ - keyList.add(entry.getKey()); - passedMapIterator.remove(); - resultCount++; - break; - } - } - - if (acsending){ - index++; - } - else{ - index--; - } - - if (resultCount>=numKeys){ - break; - } - } - // end get the list of keys - } - - return keyList; - } - - private short getOrderedValue(short[] array, int index){ - locate(array,0,array.length-1,index); - return array[index]; - } - - // sort the partitions by quick sort, and locate the target index - private void locate(short[] array, int left, int right, int index) { - - int mid=(left+right)/2; - //System.out.println(left+" to "+right+" ("+mid+")"); - - if (right==left){ - //System.out.println("* "+array[targetIndex]); - //result=array[targetIndex]; - return; - } - - if(left < right) { - short s = array[mid]; - int i = left - 1; - int j = right + 1; - - while(true) { - while(array[++i] < s) ; - while(array[--j] > s) ; - if(i >= j) - break; - swap(array, i, j); - } - - //System.out.println("2 parts: "+left+"-"+(i-1)+" and "+(j+1)+"-"+right); - - if (i>index){ - // the target index in the left partition - //System.out.println("left partition"); - locate(array, left, i-1,index); - } - else{ - // the target index in the right partition - //System.out.println("right partition"); - locate(array, j+1, right,index); - } - } - } - - private void swap(short[] array, int i, int j) { - short t = array[i]; - array[i] = array[j]; - array[j] = t; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/statistics/DataCentroid.java b/app/Locabean/src/com/musicg/math/statistics/DataCentroid.java deleted file mode 100644 index 7de62d5..0000000 --- a/app/Locabean/src/com/musicg/math/statistics/DataCentroid.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.math.statistics; - -/** - * Evaluate the centroid of an array - * - * @author Jacquet Wong - * - */ -public class DataCentroid extends MathStatistics{ - - public DataCentroid(){ - - } - - public DataCentroid(double[] values){ - setValues(values); - } - - public double evaluate(){ - double sumCentroid=0; - double sumIntensities=0; - int size=values.length; - - for (int i=0; i0){ - sumCentroid+=i*values[i]; - sumIntensities+=values[i]; - } - } - double avgCentroid=sumCentroid/sumIntensities; - - return avgCentroid; - } -} diff --git a/app/Locabean/src/com/musicg/math/statistics/MathStatistics.java b/app/Locabean/src/com/musicg/math/statistics/MathStatistics.java deleted file mode 100644 index ec07d7e..0000000 --- a/app/Locabean/src/com/musicg/math/statistics/MathStatistics.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.math.statistics; - -/** - * Abstract class for mathematics & statistics - * - * @author Jacquet Wong - * - */ -public abstract class MathStatistics{ - - protected double[] values; - - public void setValues(double[] values){ - this.values=values; - } - - public abstract double evaluate(); -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/statistics/Mean.java b/app/Locabean/src/com/musicg/math/statistics/Mean.java deleted file mode 100644 index ab988bf..0000000 --- a/app/Locabean/src/com/musicg/math/statistics/Mean.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.math.statistics; - -/** - * Evaluate the mean of an array - * @author Jacquet Wong - * - */ -public class Mean extends MathStatistics{ - - private Sum sum=new Sum(); - - public Mean(){ - } - - public Mean(double[] values){ - setValues(values); - } - - public double evaluate(){ - sum.setValues(values); - double mean=sum.evaluate()/sum.size(); - return mean; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/statistics/SpectralCentroid.java b/app/Locabean/src/com/musicg/math/statistics/SpectralCentroid.java deleted file mode 100644 index ec4a41c..0000000 --- a/app/Locabean/src/com/musicg/math/statistics/SpectralCentroid.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.math.statistics; - -/** - * Evaluate the spectral centroid of an array - * - * @author Jacquet Wong - * - */ -public class SpectralCentroid extends MathStatistics{ - - public SpectralCentroid(){ - - } - - public SpectralCentroid(double[] values){ - setValues(values); - } - - public double evaluate(){ - double sumCentroid=0; - double sumIntensities=0; - int size=values.length; - - for (int i=0; i0){ - sumCentroid+=i*values[i]; - sumIntensities+=values[i]; - } - } - double avgCentroid=sumCentroid/sumIntensities; - - return avgCentroid; - } -} diff --git a/app/Locabean/src/com/musicg/math/statistics/StandardDeviation.java b/app/Locabean/src/com/musicg/math/statistics/StandardDeviation.java deleted file mode 100644 index c2147c1..0000000 --- a/app/Locabean/src/com/musicg/math/statistics/StandardDeviation.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.math.statistics; - -/** - * Evaluate the standard deviation of an array - * - * @author Jacquet Wong - * - */ -public class StandardDeviation extends MathStatistics{ - - private Mean mean=new Mean(); - - public StandardDeviation(){ - - } - - public StandardDeviation(double[] values){ - setValues(values); - } - - public double evaluate(){ - - mean.setValues(values); - double meanValue=mean.evaluate(); - - int size=values.length; - double diffSquare=0; - double sd=Double.NaN; - - for (int i=0; i0){ - sd=Math.sqrt(diffSquare/size); - } - - return sd; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/math/statistics/Sum.java b/app/Locabean/src/com/musicg/math/statistics/Sum.java deleted file mode 100644 index 220d366..0000000 --- a/app/Locabean/src/com/musicg/math/statistics/Sum.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.math.statistics; - -/** - * Evaluate the sum of an array - * - * @author Jacquet Wong - * - */ -public class Sum extends MathStatistics{ - - public Sum(){ - } - - public Sum(double[] values){ - setValues(values); - } - - public double evaluate(){ - double sum=0; - int size=values.length; - for (int i=0 ;i=0 && signals[i+1]<0) || (signals[i]<0 && signals[i+1]>=0)){ - numZC++; - } - } - - return numZC/lengthInSecond; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/pitch/PitchHandler.java b/app/Locabean/src/com/musicg/pitch/PitchHandler.java deleted file mode 100644 index f384a9f..0000000 --- a/app/Locabean/src/com/musicg/pitch/PitchHandler.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.pitch; - -import java.util.Arrays; - -/** - * Methods for handling the pitches - * - * @author Jacquet Wong - * - */ -public class PitchHandler{ - - /** - * Get the tone changed by comparing two tones - * return 1.0 is a semi-tone, 2.0 is a tone, etc... - * - * @param f1 original tone - * @param f2 new tone - * @return tone changed - */ - public double getToneChanged(double f1, double f2){ - return Math.log(f1/f2)/Math.log(2)*12; - } - - /** - * Determine the harmonic probability of a list of frequencies - * - * @param list of frequencies - * @return probability of harmonic - */ - public double getHarmonicProbability(double[] frequencies){ - - int harmonicCount=0; - int count=0; - Arrays.sort(frequencies); - - for (int i=0; i=1){ - double minF0=100; - int minDivisor=(int)(f1/minF0); - - for (int i=1; i<=minDivisor; i++){ - double f0=f1/i; - int maxMultiplier=(int)(f2/f0+1); - for (int j=2; j<=maxMultiplier; j++){ - double f=f0*j; - double diff=Math.abs(getToneChanged(f,f2)%12); - if (diff>6) diff=12-diff; - if (diff<=1) return true; - } - } - } - - return false; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/processor/IntensityProcessor.java b/app/Locabean/src/com/musicg/processor/IntensityProcessor.java deleted file mode 100644 index b5874ee..0000000 --- a/app/Locabean/src/com/musicg/processor/IntensityProcessor.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.musicg.processor; - -public interface IntensityProcessor{ - public void execute(); - public double[][] getIntensities(); -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/processor/ProcessorChain.java b/app/Locabean/src/com/musicg/processor/ProcessorChain.java deleted file mode 100644 index bf44631..0000000 --- a/app/Locabean/src/com/musicg/processor/ProcessorChain.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.musicg.processor; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -public class ProcessorChain{ - - private double[][] intensities; - List processorList=new LinkedList(); - - public ProcessorChain(double[][] intensities){ - this.intensities=intensities; - RobustIntensityProcessor robustProcessor=new RobustIntensityProcessor(intensities,1); - processorList.add(robustProcessor); - process(); - } - - private void process(){ - Iterator iterator=processorList.iterator(); - while(iterator.hasNext()){ - IntensityProcessor processor=iterator.next(); - processor.execute(); - intensities=processor.getIntensities(); - } - } - - public double[][] getIntensities(){ - return intensities; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/processor/RobustIntensityProcessor.java b/app/Locabean/src/com/musicg/processor/RobustIntensityProcessor.java deleted file mode 100644 index d822890..0000000 --- a/app/Locabean/src/com/musicg/processor/RobustIntensityProcessor.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.musicg.processor; - -import com.musicg.math.rank.ArrayRankDouble; - -public class RobustIntensityProcessor implements IntensityProcessor{ - - private double[][] intensities; - private int numPointsPerFrame; - - public RobustIntensityProcessor(double[][] intensities, int numPointsPerFrame){ - this.intensities=intensities; - this.numPointsPerFrame=numPointsPerFrame; - } - - public void execute(){ - - int numX=intensities.length; - int numY=intensities[0].length; - double[][] processedIntensities=new double[numX][numY]; - - for (int i=0; i=passValue){ - processedIntensities[i][j]=intensities[i][j]; - } - } - } - intensities=processedIntensities; - } - - public double[][] getIntensities(){ - return intensities; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/processor/TopManyPointsProcessorChain.java b/app/Locabean/src/com/musicg/processor/TopManyPointsProcessorChain.java deleted file mode 100644 index 37663c3..0000000 --- a/app/Locabean/src/com/musicg/processor/TopManyPointsProcessorChain.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.musicg.processor; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - - -public class TopManyPointsProcessorChain{ - - private double[][] intensities; - List processorList=new LinkedList(); - - public TopManyPointsProcessorChain(double[][] intensities, int numPoints){ - this.intensities=intensities; - RobustIntensityProcessor robustProcessor=new RobustIntensityProcessor(intensities,numPoints); - processorList.add(robustProcessor); - process(); - } - - private void process(){ - Iterator iterator=processorList.iterator(); - while(iterator.hasNext()){ - IntensityProcessor processor=iterator.next(); - processor.execute(); - intensities=processor.getIntensities(); - } - } - - public double[][] getIntensities(){ - return intensities; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/properties/FingerprintProperties.java b/app/Locabean/src/com/musicg/properties/FingerprintProperties.java deleted file mode 100644 index 374cbcd..0000000 --- a/app/Locabean/src/com/musicg/properties/FingerprintProperties.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.properties; - -public class FingerprintProperties{ - - protected static FingerprintProperties instance=null; - - private int numRobustPointsPerFrame=4; // number of points in each frame, i.e. top 4 intensities in fingerprint - private int sampleSizePerFrame=2048; // number of audio samples in a frame, it is suggested to be the FFT Size - private int overlapFactor=4; // 8 means each move 1/8 nSample length. 1 means no overlap, better 1,2,4,8 ... 32 - private int numFilterBanks=4; - - private int upperBoundedFrequency=1500; // low pass - private int lowerBoundedFrequency=400; // high pass - private int fps=5; // in order to have 5fps with 2048 sampleSizePerFrame, wave's sample rate need to be 10240 (sampleSizePerFrame*fps) - private int sampleRate=sampleSizePerFrame*fps; // the audio's sample rate needed to resample to this in order to fit the sampleSizePerFrame and fps - private int numFramesInOneSecond=overlapFactor*fps; // since the overlap factor affects the actual number of fps, so this value is used to evaluate how many frames in one second eventually - - private int refMaxActivePairs=1; // max. active pairs per anchor point for reference songs - private int sampleMaxActivePairs=10; // max. active pairs per anchor point for sample clip - private int numAnchorPointsPerInterval=10; - private int anchorPointsIntervalLength=4; // in frames (5fps,4 overlap per second) - private int maxTargetZoneDistance=4; // in frame (5fps,4 overlap per second) - - private int numFrequencyUnits=(upperBoundedFrequency-lowerBoundedFrequency+1)/fps+1; // num frequency units - - public static FingerprintProperties getInstance(){ - if (instance == null){ - synchronized(FingerprintProperties.class){ - if(instance == null) { - instance = new FingerprintProperties(); - } - } - } - return instance; - } - - public int getNumRobustPointsPerFrame() { - return numRobustPointsPerFrame; - } - - public int getSampleSizePerFrame() { - return sampleSizePerFrame; - } - - public int getOverlapFactor() { - return overlapFactor; - } - - public int getNumFilterBanks() { - return numFilterBanks; - } - - public int getUpperBoundedFrequency() { - return upperBoundedFrequency; - } - - public int getLowerBoundedFrequency() { - return lowerBoundedFrequency; - } - - public int getFps() { - return fps; - } - - public int getRefMaxActivePairs() { - return refMaxActivePairs; - } - - public int getSampleMaxActivePairs() { - return sampleMaxActivePairs; - } - - public int getNumAnchorPointsPerInterval() { - return numAnchorPointsPerInterval; - } - - public int getAnchorPointsIntervalLength() { - return anchorPointsIntervalLength; - } - - public int getMaxTargetZoneDistance() { - return maxTargetZoneDistance; - } - - public int getNumFrequencyUnits() { - return numFrequencyUnits; - } - - public int getMaxPossiblePairHashcode(){ - return maxTargetZoneDistance*numFrequencyUnits*numFrequencyUnits+numFrequencyUnits*numFrequencyUnits+numFrequencyUnits; - } - - public int getSampleRate() { - return sampleRate; - } - - public int getNumFramesInOneSecond() { - return numFramesInOneSecond; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/serialization/ObjectSerializer.java b/app/Locabean/src/com/musicg/serialization/ObjectSerializer.java deleted file mode 100644 index 83964e6..0000000 --- a/app/Locabean/src/com/musicg/serialization/ObjectSerializer.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.musicg.serialization; - -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -public class ObjectSerializer{ - - public ObjectSerializer(){ - } - - public void dump(Object object, String dumpFile){ - // serialize the object - try { - FileOutputStream fout = new FileOutputStream(dumpFile); - ObjectOutputStream oos = new ObjectOutputStream(fout); - oos.writeObject(object); - oos.close(); - fout.close(); - } - catch (Exception e) { - e.printStackTrace(); - } - } - - public Object load(String dumpFile){ - - Object object=null; - - // load the memory - try { - FileInputStream fin=new FileInputStream(dumpFile); - ObjectInputStream ois=new ObjectInputStream(fin); - object=ois.readObject(); - ois.close(); - fin.close(); - } - catch (Exception e) { - e.printStackTrace(); - } - - return object; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/version/Version.java b/app/Locabean/src/com/musicg/version/Version.java deleted file mode 100644 index 1a640ea..0000000 --- a/app/Locabean/src/com/musicg/version/Version.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.musicg.version; - -public class Version{ - public static final String VERSION="1.4.2.0"; -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/wave/Wave.java b/app/Locabean/src/com/musicg/wave/Wave.java deleted file mode 100644 index 4a01fcf..0000000 --- a/app/Locabean/src/com/musicg/wave/Wave.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * 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; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; - -import com.musicg.fingerprint.FingerprintManager; -import com.musicg.fingerprint.FingerprintSimilarity; -import com.musicg.fingerprint.FingerprintSimilarityComputer; -import com.musicg.wave.extension.NormalizedSampleAmplitudes; -import com.musicg.wave.extension.Spectrogram; - -/** - * Read WAVE headers and data from wave input stream - * - * @author Jacquet Wong - */ -public class Wave implements Serializable { - - private static final long serialVersionUID = 1L; - private WaveHeader waveHeader; - private byte[] data; // little endian - private byte[] fingerprint; - - /** - * Constructor - * - */ - public Wave() { - this.waveHeader = new WaveHeader(); - this.data = new byte[0]; - } - - /** - * Constructor - * - * @param filename - * Wave file - */ - public Wave(String filename) { - try { - InputStream inputStream = new FileInputStream(filename); - initWaveWithInputStream(inputStream); - inputStream.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Constructor - * - * @param inputStream - * Wave file input stream - */ - public Wave(InputStream inputStream) { - initWaveWithInputStream(inputStream); - } - - /** - * Constructor - * - * @param WaveHeader - * waveHeader - * @param byte[] data - */ - public Wave(WaveHeader waveHeader, byte[] data) { - this.waveHeader = waveHeader; - this.data = data; - } - - private void initWaveWithInputStream(InputStream inputStream) { - // reads the first 44 bytes for header - waveHeader = new WaveHeader(inputStream); - - if (waveHeader.isValid()) { - // load data - try { - data = new byte[inputStream.available()]; - inputStream.read(data); - } catch (IOException e) { - e.printStackTrace(); - } - // end load data - } else { - System.err.println("Invalid Wave Header"); - } - } - - /** - * Trim the wave data - * - * @param leftTrimNumberOfSample - * Number of sample trimmed from beginning - * @param rightTrimNumberOfSample - * Number of sample trimmed from ending - */ - public void trim(int leftTrimNumberOfSample, int rightTrimNumberOfSample) { - - long chunkSize = waveHeader.getChunkSize(); - long subChunk2Size = waveHeader.getSubChunk2Size(); - - long totalTrimmed = leftTrimNumberOfSample + rightTrimNumberOfSample; - - if (totalTrimmed > subChunk2Size) { - leftTrimNumberOfSample = (int) subChunk2Size; - } - - // update wav info - chunkSize -= totalTrimmed; - subChunk2Size -= totalTrimmed; - - if (chunkSize >= 0 && subChunk2Size >= 0) { - waveHeader.setChunkSize(chunkSize); - waveHeader.setSubChunk2Size(subChunk2Size); - - byte[] trimmedData = new byte[(int) subChunk2Size]; - System.arraycopy(data, (int) leftTrimNumberOfSample, trimmedData, - 0, (int) subChunk2Size); - data = trimmedData; - } else { - System.err.println("Trim error: Negative length"); - } - } - - /** - * Trim the wave data from beginning - * - * @param numberOfSample - * numberOfSample trimmed from beginning - */ - public void leftTrim(int numberOfSample) { - trim(numberOfSample, 0); - } - - /** - * Trim the wave data from ending - * - * @param numberOfSample - * numberOfSample trimmed from ending - */ - public void rightTrim(int numberOfSample) { - trim(0, numberOfSample); - } - - /** - * Trim the wave data - * - * @param leftTrimSecond - * Seconds trimmed from beginning - * @param rightTrimSecond - * Seconds trimmed from ending - */ - public void trim(double leftTrimSecond, double rightTrimSecond) { - - int sampleRate = waveHeader.getSampleRate(); - int bitsPerSample = waveHeader.getBitsPerSample(); - int channels = waveHeader.getChannels(); - - int leftTrimNumberOfSample = (int) (sampleRate * bitsPerSample / 8 - * channels * leftTrimSecond); - int rightTrimNumberOfSample = (int) (sampleRate * bitsPerSample / 8 - * channels * rightTrimSecond); - - trim(leftTrimNumberOfSample, rightTrimNumberOfSample); - } - - /** - * Trim the wave data from beginning - * - * @param second - * Seconds trimmed from beginning - */ - public void leftTrim(double second) { - trim(second, 0); - } - - /** - * Trim the wave data from ending - * - * @param second - * Seconds trimmed from ending - */ - public void rightTrim(double second) { - trim(0, second); - } - - /** - * Get the wave header - * - * @return waveHeader - */ - public WaveHeader getWaveHeader() { - return waveHeader; - } - - /** - * Get the wave spectrogram - * - * @return spectrogram - */ - public Spectrogram getSpectrogram() { - return new Spectrogram(this); - } - - /** - * Get the wave spectrogram - * - * @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 - * - * @return spectrogram - */ - public Spectrogram getSpectrogram(int fftSampleSize, int overlapFactor) { - return new Spectrogram(this, fftSampleSize, overlapFactor); - } - - /** - * Get the wave data in bytes - * - * @return wave data - */ - public byte[] getBytes() { - return data; - } - - /** - * Data byte size of the wave excluding header size - * - * @return byte size of the wave - */ - public int size() { - return data.length; - } - - /** - * Length of the wave in second - * - * @return length in second - */ - public float length() { - float second = (float) waveHeader.getSubChunk2Size() - / waveHeader.getByteRate(); - return second; - } - - /** - * Timestamp of the wave length - * - * @return timestamp - */ - public String timestamp() { - float totalSeconds = this.length(); - float second = totalSeconds % 60; - int minute = (int) totalSeconds / 60 % 60; - int hour = (int) (totalSeconds / 3600); - - StringBuffer sb = new StringBuffer(); - if (hour > 0) { - sb.append(hour + ":"); - } - if (minute > 0) { - sb.append(minute + ":"); - } - sb.append(second); - - return sb.toString(); - } - - /** - * Get the amplitudes of the wave samples (depends on the header) - * - * @return amplitudes array (signed 16-bit) - */ - public short[] getSampleAmplitudes() { - int bytePerSample = waveHeader.getBitsPerSample() / 8; - int numSamples = data.length / bytePerSample; - short[] amplitudes = new short[numSamples]; - - int pointer = 0; - for (int i = 0; i < numSamples; i++) { - short amplitude = 0; - for (int byteNumber = 0; byteNumber < bytePerSample; byteNumber++) { - // little endian - amplitude |= (short) ((data[pointer++] & 0xFF) << (byteNumber * 8)); - } - amplitudes[i] = amplitude; - } - - return amplitudes; - } - - public String toString() { - StringBuffer sb = new StringBuffer(waveHeader.toString()); - sb.append("\n"); - sb.append("length: " + timestamp()); - return sb.toString(); - } - - public double[] getNormalizedAmplitudes() { - NormalizedSampleAmplitudes amplitudes = new NormalizedSampleAmplitudes( - this); - return amplitudes.getNormalizedAmplitudes(); - } - - public byte[] getFingerprint() { - if (fingerprint == null) { - FingerprintManager fingerprintManager = new FingerprintManager(); - fingerprint = fingerprintManager.extractFingerprint(this); - } - return fingerprint; - } - - public FingerprintSimilarity getFingerprintSimilarity(Wave wave) { - FingerprintSimilarityComputer fingerprintSimilarityComputer = new FingerprintSimilarityComputer( - this.getFingerprint(), wave.getFingerprint()); - return fingerprintSimilarityComputer.getFingerprintsSimilarity(); - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/wave/WaveFileManager.java b/app/Locabean/src/com/musicg/wave/WaveFileManager.java deleted file mode 100644 index 5264c1c..0000000 --- a/app/Locabean/src/com/musicg/wave/WaveFileManager.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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; - -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; - - -public class WaveFileManager{ - - private Wave wave; - - public WaveFileManager(){ - wave=new Wave(); - } - - public WaveFileManager(Wave wave){ - setWave(wave); - } - - /** - * Save the wave file - * - * @param filename - * filename to be saved - * - * @see wave file saved - */ - public void saveWaveAsFile(String filename){ - - WaveHeader waveHeader=wave.getWaveHeader(); - - int byteRate = waveHeader.getByteRate(); - int audioFormat = waveHeader.getAudioFormat(); - int sampleRate = waveHeader.getSampleRate(); - int bitsPerSample = waveHeader.getBitsPerSample(); - int channels = waveHeader.getChannels(); - long chunkSize = waveHeader.getChunkSize(); - long subChunk1Size = waveHeader.getSubChunk1Size(); - long subChunk2Size = waveHeader.getSubChunk2Size(); - int blockAlign = waveHeader.getBlockAlign(); - - try { - FileOutputStream fos = new FileOutputStream(filename); - fos.write(WaveHeader.RIFF_HEADER.getBytes()); - // little endian - fos.write(new byte[] { (byte) (chunkSize), (byte) (chunkSize >> 8), - (byte) (chunkSize >> 16), (byte) (chunkSize >> 24) }); - fos.write(WaveHeader.WAVE_HEADER.getBytes()); - fos.write(WaveHeader.FMT_HEADER.getBytes()); - fos.write(new byte[] { (byte) (subChunk1Size), - (byte) (subChunk1Size >> 8), (byte) (subChunk1Size >> 16), - (byte) (subChunk1Size >> 24) }); - fos.write(new byte[] { (byte) (audioFormat), - (byte) (audioFormat >> 8) }); - fos.write(new byte[] { (byte) (channels), (byte) (channels >> 8) }); - fos.write(new byte[] { (byte) (sampleRate), - (byte) (sampleRate >> 8), (byte) (sampleRate >> 16), - (byte) (sampleRate >> 24) }); - fos.write(new byte[] { (byte) (byteRate), (byte) (byteRate >> 8), - (byte) (byteRate >> 16), (byte) (byteRate >> 24) }); - fos.write(new byte[] { (byte) (blockAlign), - (byte) (blockAlign >> 8) }); - fos.write(new byte[] { (byte) (bitsPerSample), - (byte) (bitsPerSample >> 8) }); - fos.write(WaveHeader.DATA_HEADER.getBytes()); - fos.write(new byte[] { (byte) (subChunk2Size), - (byte) (subChunk2Size >> 8), (byte) (subChunk2Size >> 16), - (byte) (subChunk2Size >> 24) }); - fos.write(wave.getBytes()); - fos.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public Wave getWave() { - return wave; - } - - public void setWave(Wave wave) { - this.wave = wave; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/wave/WaveHeader.java b/app/Locabean/src/com/musicg/wave/WaveHeader.java deleted file mode 100644 index 982cd78..0000000 --- a/app/Locabean/src/com/musicg/wave/WaveHeader.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * 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; - -import java.io.IOException; -import java.io.InputStream; - -/** - * WAV File Specification - * https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ - * - * @author Jacquet Wong - */ -public class WaveHeader { - - public static final String RIFF_HEADER = "RIFF"; - public static final String WAVE_HEADER = "WAVE"; - public static final String FMT_HEADER = "fmt "; - public static final String DATA_HEADER = "data"; - public static final int HEADER_BYTE_LENGTH = 44; // 44 bytes for header - - private boolean valid; - private String chunkId; // 4 bytes - private long chunkSize; // unsigned 4 bytes, little endian - private String format; // 4 bytes - private String subChunk1Id; // 4 bytes - private long subChunk1Size; // unsigned 4 bytes, little endian - private int audioFormat; // unsigned 2 bytes, little endian - private int channels; // unsigned 2 bytes, little endian - private long sampleRate; // unsigned 4 bytes, little endian - private long byteRate; // unsigned 4 bytes, little endian - private int blockAlign; // unsigned 2 bytes, little endian - private int bitsPerSample; // unsigned 2 bytes, little endian - private String subChunk2Id; // 4 bytes - private long subChunk2Size; // unsigned 4 bytes, little endian - - public WaveHeader(){ - // init a 8k 16bit mono wav - chunkSize=36; - subChunk1Size=16; - audioFormat=1; - channels=1; - sampleRate=8000; - byteRate=16000; - blockAlign=2; - bitsPerSample=16; - subChunk2Size=0; - valid=true; - } - - public WaveHeader(InputStream inputStream) { - valid = loadHeader(inputStream); - } - - private boolean loadHeader(InputStream inputStream) { - - byte[] headerBuffer = new byte[HEADER_BYTE_LENGTH]; - try { - inputStream.read(headerBuffer); - - // read header - int pointer = 0; - chunkId = new String(new byte[] { headerBuffer[pointer++], - headerBuffer[pointer++], headerBuffer[pointer++], - headerBuffer[pointer++] }); - // little endian - chunkSize = (long) (headerBuffer[pointer++] & 0xff) - | (long) (headerBuffer[pointer++] & 0xff) << 8 - | (long) (headerBuffer[pointer++] & 0xff) << 16 - | (long) (headerBuffer[pointer++] & 0xff << 24); - format = new String(new byte[] { headerBuffer[pointer++], - headerBuffer[pointer++], headerBuffer[pointer++], - headerBuffer[pointer++] }); - subChunk1Id = new String(new byte[] { headerBuffer[pointer++], - headerBuffer[pointer++], headerBuffer[pointer++], - headerBuffer[pointer++] }); - subChunk1Size = (long) (headerBuffer[pointer++] & 0xff) - | (long) (headerBuffer[pointer++] & 0xff) << 8 - | (long) (headerBuffer[pointer++] & 0xff) << 16 - | (long) (headerBuffer[pointer++] & 0xff) << 24; - audioFormat = (int) ((headerBuffer[pointer++] & 0xff) | (headerBuffer[pointer++] & 0xff) << 8); - channels = (int) ((headerBuffer[pointer++] & 0xff) | (headerBuffer[pointer++] & 0xff) << 8); - sampleRate = (long) (headerBuffer[pointer++] & 0xff) - | (long) (headerBuffer[pointer++] & 0xff) << 8 - | (long) (headerBuffer[pointer++] & 0xff) << 16 - | (long) (headerBuffer[pointer++] & 0xff) << 24; - byteRate = (long) (headerBuffer[pointer++] & 0xff) - | (long) (headerBuffer[pointer++] & 0xff) << 8 - | (long) (headerBuffer[pointer++] & 0xff) << 16 - | (long) (headerBuffer[pointer++] & 0xff) << 24; - blockAlign = (int) ((headerBuffer[pointer++] & 0xff) | (headerBuffer[pointer++] & 0xff) << 8); - bitsPerSample = (int) ((headerBuffer[pointer++] & 0xff) | (headerBuffer[pointer++] & 0xff) << 8); - subChunk2Id = new String(new byte[] { headerBuffer[pointer++], - headerBuffer[pointer++], headerBuffer[pointer++], - headerBuffer[pointer++] }); - subChunk2Size = (long) (headerBuffer[pointer++] & 0xff) - | (long) (headerBuffer[pointer++] & 0xff) << 8 - | (long) (headerBuffer[pointer++] & 0xff) << 16 - | (long) (headerBuffer[pointer++] & 0xff) << 24; - // end read header - - // the inputStream should be closed outside this method - - // dis.close(); - - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - if (bitsPerSample!=8 && bitsPerSample!=16){ - System.err.println("WaveHeader: only supports bitsPerSample 8 or 16"); - return false; - } - - // check the format is support - if (chunkId.toUpperCase().equals(RIFF_HEADER) - && format.toUpperCase().equals(WAVE_HEADER) && audioFormat == 1) { - return true; - } - else{ - System.err.println("WaveHeader: Unsupported header format"); - } - - return false; - } - - public boolean isValid() { - return valid; - } - - public String getChunkId() { - return chunkId; - } - - public long getChunkSize() { - return chunkSize; - } - - public String getFormat() { - return format; - } - - public String getSubChunk1Id() { - return subChunk1Id; - } - - public long getSubChunk1Size() { - return subChunk1Size; - } - - public int getAudioFormat() { - return audioFormat; - } - - public int getChannels() { - return channels; - } - - public int getSampleRate() { - return (int) sampleRate; - } - - public int getByteRate() { - return (int) byteRate; - } - - public int getBlockAlign() { - return blockAlign; - } - - public int getBitsPerSample() { - return bitsPerSample; - } - - public String getSubChunk2Id() { - return subChunk2Id; - } - - public long getSubChunk2Size() { - return subChunk2Size; - } - - public void setSampleRate(int sampleRate){ - int newSubChunk2Size = (int)(this.subChunk2Size * sampleRate / this.sampleRate); - // if num bytes for each sample is even, the size of newSubChunk2Size also needed to be in even number - if ((bitsPerSample/8)%2==0){ - if (newSubChunk2Size%2!=0){ - newSubChunk2Size++; - } - } - - this.sampleRate = sampleRate; - this.byteRate = sampleRate*bitsPerSample/8; - this.chunkSize = newSubChunk2Size+36; - this.subChunk2Size = newSubChunk2Size; - } - - public void setChunkId(String chunkId) { - this.chunkId = chunkId; - } - - public void setChunkSize(long chunkSize) { - this.chunkSize = chunkSize; - } - - public void setFormat(String format) { - this.format = format; - } - - public void setSubChunk1Id(String subChunk1Id) { - this.subChunk1Id = subChunk1Id; - } - - public void setSubChunk1Size(long subChunk1Size) { - this.subChunk1Size = subChunk1Size; - } - - public void setAudioFormat(int audioFormat) { - this.audioFormat = audioFormat; - } - - public void setChannels(int channels) { - this.channels = channels; - } - - public void setByteRate(long byteRate) { - this.byteRate = byteRate; - } - - public void setBlockAlign(int blockAlign) { - this.blockAlign = blockAlign; - } - - public void setBitsPerSample(int bitsPerSample) { - this.bitsPerSample = bitsPerSample; - } - - public void setSubChunk2Id(String subChunk2Id) { - this.subChunk2Id = subChunk2Id; - } - - public void setSubChunk2Size(long subChunk2Size) { - this.subChunk2Size = subChunk2Size; - } - - public String toString() { - - StringBuffer sb = new StringBuffer(); - sb.append("chunkId: " + chunkId); - sb.append("\n"); - sb.append("chunkSize: " + chunkSize); - sb.append("\n"); - sb.append("format: " + format); - sb.append("\n"); - sb.append("subChunk1Id: " + subChunk1Id); - sb.append("\n"); - sb.append("subChunk1Size: " + subChunk1Size); - sb.append("\n"); - sb.append("audioFormat: " + audioFormat); - sb.append("\n"); - sb.append("channels: " + channels); - sb.append("\n"); - sb.append("sampleRate: " + sampleRate); - sb.append("\n"); - sb.append("byteRate: " + byteRate); - sb.append("\n"); - sb.append("blockAlign: " + blockAlign); - sb.append("\n"); - sb.append("bitsPerSample: " + bitsPerSample); - sb.append("\n"); - sb.append("subChunk2Id: " + subChunk2Id); - sb.append("\n"); - sb.append("subChunk2Size: " + subChunk2Size); - return sb.toString(); - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/wave/WaveTypeDetector.java b/app/Locabean/src/com/musicg/wave/WaveTypeDetector.java deleted file mode 100644 index d68612d..0000000 --- a/app/Locabean/src/com/musicg/wave/WaveTypeDetector.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.musicg.wave; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.ArrayList; - -import com.musicg.api.WhistleApi; - -public class WaveTypeDetector { - - private Wave wave; - - public WaveTypeDetector(Wave wave) { - this.wave = wave; - } - - public double getWhistleProbability() { - - double probability = 0; - - WaveHeader wavHeader = wave.getWaveHeader(); - - // fft size 1024, no overlap - int fftSampleSize = 1024; - int fftSignalByteLength = fftSampleSize * wavHeader.getBitsPerSample() / 8; - byte[] audioBytes = wave.getBytes(); - ByteArrayInputStream inputStream = new ByteArrayInputStream(audioBytes); - - WhistleApi whistleApi = new WhistleApi(wavHeader); - - // read the byte signals - try { - int numFrames = inputStream.available() / fftSignalByteLength; - byte[] bytes = new byte[fftSignalByteLength]; - int checkLength = 3; - int passScore = 3; - - ArrayList bufferList = new ArrayList(); - int numWhistles = 0; - int numPasses = 0; - - // first 10(checkLength) frames - for (int frameNumber = 0; frameNumber < checkLength; frameNumber++) { - inputStream.read(bytes); - boolean isWhistle = whistleApi.isWhistle(bytes); - bufferList.add(isWhistle); - if (isWhistle) { - numWhistles++; - } - if (numWhistles >= passScore) { - numPasses++; - } - // System.out.println(frameNumber+": "+numWhistles); - } - - // other frames - for (int frameNumber = checkLength; frameNumber < numFrames; frameNumber++) { - inputStream.read(bytes); - boolean isWhistle = whistleApi.isWhistle(bytes); - if (bufferList.get(0)) { - numWhistles--; - } - bufferList.remove(0); - bufferList.add(isWhistle); - - if (isWhistle) { - numWhistles++; - } - if (numWhistles >= passScore) { - numPasses++; - } - // System.out.println(frameNumber+": "+numWhistles); - } - probability = (double) numPasses / numFrames; - - } catch (IOException e) { - e.printStackTrace(); - } - - return probability; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/wave/extension/NormalizedSampleAmplitudes.java b/app/Locabean/src/com/musicg/wave/extension/NormalizedSampleAmplitudes.java deleted file mode 100644 index d4fabd7..0000000 --- a/app/Locabean/src/com/musicg/wave/extension/NormalizedSampleAmplitudes.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.wave.Wave; - -/** - * Handles the wave data in amplitude-time domain. - * - * @author Jacquet Wong - */ -public class NormalizedSampleAmplitudes{ - - private Wave wave; - private double[] normalizedAmplitudes; // normalizedAmplitudes[sampleNumber]=normalizedAmplitudeInTheFrame - - public NormalizedSampleAmplitudes(Wave wave){ - this.wave=wave; - } - - /** - * - * Get normalized amplitude of each frame - * - * @return array of normalized amplitudes(signed 16 bit): normalizedAmplitudes[frame]=amplitude - */ - public double[] getNormalizedAmplitudes() { - - if (normalizedAmplitudes == null) { - - boolean signed=true; - - // usually 8bit is unsigned - if (wave.getWaveHeader().getBitsPerSample()==8){ - signed=false; - } - - short[] amplitudes=wave.getSampleAmplitudes(); - int numSamples = amplitudes.length; - int maxAmplitude = 1 << (wave.getWaveHeader().getBitsPerSample() - 1); - - if (!signed){ // one more bit for unsigned value - maxAmplitude<<=1; - } - - normalizedAmplitudes = new double[numSamples]; - for (int i = 0; i < numSamples; i++) { - normalizedAmplitudes[i] = (double) amplitudes[i] / maxAmplitude; - } - } - return normalizedAmplitudes; - } -} \ No newline at end of file diff --git a/app/Locabean/src/com/musicg/wave/extension/Spectrogram.java b/app/Locabean/src/com/musicg/wave/extension/Spectrogram.java deleted file mode 100644 index a4d1b5a..0000000 --- a/app/Locabean/src/com/musicg/wave/extension/Spectrogram.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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]> 1); - double wfr = Math.cos(arg); - double wfi = sign * Math.sin(arg); - - for (int j = 0; j < jmax; j += 2) { - warray[w_index++] = wr; - warray[w_index++] = wi; - - double tempr = wr; - wr = tempr * wfr - wi * wfi; - wi = tempr * wfi + wi * wfr; - } - } - - // PRECOMPUTATION of wwr1, wwi1 for factor 4 Decomposition (3 * complex - // operators and 8 +/- complex operators) - { - w_index = 0; - int w_index2 = warray.length >> 1; - for (int i = 0, nstep = 2; i < (imax - 1); i++) { - int jmax = nstep; - nstep *= 2; - - int ii = w_index + jmax; - for (int j = 0; j < jmax; j += 2) { - double wr = warray[w_index++]; - double wi = warray[w_index++]; - double wr1 = warray[ii++]; - double wi1 = warray[ii++]; - warray[w_index2++] = wr * wr1 - wi * wi1; - warray[w_index2++] = wr * wi1 + wi * wr1; - } - } - - } - - return warray; - } - - private final static void calc(int fftFrameSize, double[] data, int sign, - double[] w) { - - final int fftFrameSize2 = fftFrameSize << 1; - - int nstep = 2; - - if (nstep >= fftFrameSize2) - return; - int i = nstep - 2; - if (sign == -1) - calcF4F(fftFrameSize, data, i, nstep, w); - else - calcF4I(fftFrameSize, data, i, nstep, w); - - } - - private final static void calcF2E(int fftFrameSize, double[] data, int i, - int nstep, double[] w) { - int jmax = nstep; - for (int n = 0; n < jmax; n += 2) { - double wr = w[i++]; - double wi = w[i++]; - int m = n + jmax; - double datam_r = data[m]; - double datam_i = data[m + 1]; - double datan_r = data[n]; - double datan_i = data[n + 1]; - double tempr = datam_r * wr - datam_i * wi; - double tempi = datam_r * wi + datam_i * wr; - data[m] = datan_r - tempr; - data[m + 1] = datan_i - tempi; - data[n] = datan_r + tempr; - data[n + 1] = datan_i + tempi; - } - return; - - } - - // Perform Factor-4 Decomposition with 3 * complex operators and 8 +/- - // complex operators - private final static void calcF4F(int fftFrameSize, double[] data, int i, - int nstep, double[] w) { - final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize; - // Factor-4 Decomposition - - int w_len = w.length >> 1; - while (nstep < fftFrameSize2) { - - if (nstep << 2 == fftFrameSize2) { - // Goto Factor-4 Final Decomposition - // calcF4E(data, i, nstep, -1, w); - calcF4FE(fftFrameSize, data, i, nstep, w); - return; - } - int jmax = nstep; - int nnstep = nstep << 1; - if (nnstep == fftFrameSize2) { - // Factor-4 Decomposition not possible - calcF2E(fftFrameSize, data, i, nstep, w); - return; - } - nstep <<= 2; - int ii = i + jmax; - int iii = i + w_len; - - { - i += 2; - ii += 2; - iii += 2; - - for (int n = 0; n < fftFrameSize2; n += nstep) { - int m = n + jmax; - - double datam1_r = data[m]; - double datam1_i = data[m + 1]; - double datan1_r = data[n]; - double datan1_i = data[n + 1]; - - n += nnstep; - m += nnstep; - double datam2_r = data[m]; - double datam2_i = data[m + 1]; - double datan2_r = data[n]; - double datan2_i = data[n + 1]; - - double tempr = datam1_r; - double tempi = datam1_i; - - datam1_r = datan1_r - tempr; - datam1_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - double n2w1r = datan2_r; - double n2w1i = datan2_i; - double m2ww1r = datam2_r; - double m2ww1i = datam2_i; - - tempr = m2ww1r - n2w1r; - tempi = m2ww1i - n2w1i; - - datam2_r = datam1_r + tempi; - datam2_i = datam1_i - tempr; - datam1_r = datam1_r - tempi; - datam1_i = datam1_i + tempr; - - tempr = n2w1r + m2ww1r; - tempi = n2w1i + m2ww1i; - - datan2_r = datan1_r - tempr; - datan2_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - data[m] = datam2_r; - data[m + 1] = datam2_i; - data[n] = datan2_r; - data[n + 1] = datan2_i; - - n -= nnstep; - m -= nnstep; - data[m] = datam1_r; - data[m + 1] = datam1_i; - data[n] = datan1_r; - data[n + 1] = datan1_i; - - } - } - - for (int j = 2; j < jmax; j += 2) { - double wr = w[i++]; - double wi = w[i++]; - double wr1 = w[ii++]; - double wi1 = w[ii++]; - double wwr1 = w[iii++]; - double wwi1 = w[iii++]; - // double wwr1 = wr * wr1 - wi * wi1; // these numbers can be - // precomputed!!! - // double wwi1 = wr * wi1 + wi * wr1; - - for (int n = j; n < fftFrameSize2; n += nstep) { - int m = n + jmax; - - double datam1_r = data[m]; - double datam1_i = data[m + 1]; - double datan1_r = data[n]; - double datan1_i = data[n + 1]; - - n += nnstep; - m += nnstep; - double datam2_r = data[m]; - double datam2_i = data[m + 1]; - double datan2_r = data[n]; - double datan2_i = data[n + 1]; - - double tempr = datam1_r * wr - datam1_i * wi; - double tempi = datam1_r * wi + datam1_i * wr; - - datam1_r = datan1_r - tempr; - datam1_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - double n2w1r = datan2_r * wr1 - datan2_i * wi1; - double n2w1i = datan2_r * wi1 + datan2_i * wr1; - double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1; - double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1; - - tempr = m2ww1r - n2w1r; - tempi = m2ww1i - n2w1i; - - datam2_r = datam1_r + tempi; - datam2_i = datam1_i - tempr; - datam1_r = datam1_r - tempi; - datam1_i = datam1_i + tempr; - - tempr = n2w1r + m2ww1r; - tempi = n2w1i + m2ww1i; - - datan2_r = datan1_r - tempr; - datan2_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - data[m] = datam2_r; - data[m + 1] = datam2_i; - data[n] = datan2_r; - data[n + 1] = datan2_i; - - n -= nnstep; - m -= nnstep; - data[m] = datam1_r; - data[m + 1] = datam1_i; - data[n] = datan1_r; - data[n + 1] = datan1_i; - } - } - - i += jmax << 1; - - } - - calcF2E(fftFrameSize, data, i, nstep, w); - - } - - // Perform Factor-4 Decomposition with 3 * complex operators and 8 +/- - // complex operators - private final static void calcF4I(int fftFrameSize, double[] data, int i, - int nstep, double[] w) { - final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize; - // Factor-4 Decomposition - - int w_len = w.length >> 1; - while (nstep < fftFrameSize2) { - - if (nstep << 2 == fftFrameSize2) { - // Goto Factor-4 Final Decomposition - // calcF4E(data, i, nstep, 1, w); - calcF4IE(fftFrameSize, data, i, nstep, w); - return; - } - int jmax = nstep; - int nnstep = nstep << 1; - if (nnstep == fftFrameSize2) { - // Factor-4 Decomposition not possible - calcF2E(fftFrameSize, data, i, nstep, w); - return; - } - nstep <<= 2; - int ii = i + jmax; - int iii = i + w_len; - { - i += 2; - ii += 2; - iii += 2; - - for (int n = 0; n < fftFrameSize2; n += nstep) { - int m = n + jmax; - - double datam1_r = data[m]; - double datam1_i = data[m + 1]; - double datan1_r = data[n]; - double datan1_i = data[n + 1]; - - n += nnstep; - m += nnstep; - double datam2_r = data[m]; - double datam2_i = data[m + 1]; - double datan2_r = data[n]; - double datan2_i = data[n + 1]; - - double tempr = datam1_r; - double tempi = datam1_i; - - datam1_r = datan1_r - tempr; - datam1_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - double n2w1r = datan2_r; - double n2w1i = datan2_i; - double m2ww1r = datam2_r; - double m2ww1i = datam2_i; - - tempr = n2w1r - m2ww1r; - tempi = n2w1i - m2ww1i; - - datam2_r = datam1_r + tempi; - datam2_i = datam1_i - tempr; - datam1_r = datam1_r - tempi; - datam1_i = datam1_i + tempr; - - tempr = n2w1r + m2ww1r; - tempi = n2w1i + m2ww1i; - - datan2_r = datan1_r - tempr; - datan2_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - data[m] = datam2_r; - data[m + 1] = datam2_i; - data[n] = datan2_r; - data[n + 1] = datan2_i; - - n -= nnstep; - m -= nnstep; - data[m] = datam1_r; - data[m + 1] = datam1_i; - data[n] = datan1_r; - data[n + 1] = datan1_i; - - } - - } - for (int j = 2; j < jmax; j += 2) { - double wr = w[i++]; - double wi = w[i++]; - double wr1 = w[ii++]; - double wi1 = w[ii++]; - double wwr1 = w[iii++]; - double wwi1 = w[iii++]; - // double wwr1 = wr * wr1 - wi * wi1; // these numbers can be - // precomputed!!! - // double wwi1 = wr * wi1 + wi * wr1; - - for (int n = j; n < fftFrameSize2; n += nstep) { - int m = n + jmax; - - double datam1_r = data[m]; - double datam1_i = data[m + 1]; - double datan1_r = data[n]; - double datan1_i = data[n + 1]; - - n += nnstep; - m += nnstep; - double datam2_r = data[m]; - double datam2_i = data[m + 1]; - double datan2_r = data[n]; - double datan2_i = data[n + 1]; - - double tempr = datam1_r * wr - datam1_i * wi; - double tempi = datam1_r * wi + datam1_i * wr; - - datam1_r = datan1_r - tempr; - datam1_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - double n2w1r = datan2_r * wr1 - datan2_i * wi1; - double n2w1i = datan2_r * wi1 + datan2_i * wr1; - double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1; - double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1; - - tempr = n2w1r - m2ww1r; - tempi = n2w1i - m2ww1i; - - datam2_r = datam1_r + tempi; - datam2_i = datam1_i - tempr; - datam1_r = datam1_r - tempi; - datam1_i = datam1_i + tempr; - - tempr = n2w1r + m2ww1r; - tempi = n2w1i + m2ww1i; - - datan2_r = datan1_r - tempr; - datan2_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - data[m] = datam2_r; - data[m + 1] = datam2_i; - data[n] = datan2_r; - data[n + 1] = datan2_i; - - n -= nnstep; - m -= nnstep; - data[m] = datam1_r; - data[m + 1] = datam1_i; - data[n] = datan1_r; - data[n + 1] = datan1_i; - - } - } - - i += jmax << 1; - - } - - calcF2E(fftFrameSize, data, i, nstep, w); - - } - - // Perform Factor-4 Decomposition with 3 * complex operators and 8 +/- - // complex operators - private final static void calcF4FE(int fftFrameSize, double[] data, int i, - int nstep, double[] w) { - final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize; - // Factor-4 Decomposition - - int w_len = w.length >> 1; - while (nstep < fftFrameSize2) { - - int jmax = nstep; - int nnstep = nstep << 1; - if (nnstep == fftFrameSize2) { - // Factor-4 Decomposition not possible - calcF2E(fftFrameSize, data, i, nstep, w); - return; - } - nstep <<= 2; - int ii = i + jmax; - int iii = i + w_len; - for (int n = 0; n < jmax; n += 2) { - double wr = w[i++]; - double wi = w[i++]; - double wr1 = w[ii++]; - double wi1 = w[ii++]; - double wwr1 = w[iii++]; - double wwi1 = w[iii++]; - // double wwr1 = wr * wr1 - wi * wi1; // these numbers can be - // precomputed!!! - // double wwi1 = wr * wi1 + wi * wr1; - - int m = n + jmax; - - double datam1_r = data[m]; - double datam1_i = data[m + 1]; - double datan1_r = data[n]; - double datan1_i = data[n + 1]; - - n += nnstep; - m += nnstep; - double datam2_r = data[m]; - double datam2_i = data[m + 1]; - double datan2_r = data[n]; - double datan2_i = data[n + 1]; - - double tempr = datam1_r * wr - datam1_i * wi; - double tempi = datam1_r * wi + datam1_i * wr; - - datam1_r = datan1_r - tempr; - datam1_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - double n2w1r = datan2_r * wr1 - datan2_i * wi1; - double n2w1i = datan2_r * wi1 + datan2_i * wr1; - double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1; - double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1; - - tempr = m2ww1r - n2w1r; - tempi = m2ww1i - n2w1i; - - datam2_r = datam1_r + tempi; - datam2_i = datam1_i - tempr; - datam1_r = datam1_r - tempi; - datam1_i = datam1_i + tempr; - - tempr = n2w1r + m2ww1r; - tempi = n2w1i + m2ww1i; - - datan2_r = datan1_r - tempr; - datan2_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - data[m] = datam2_r; - data[m + 1] = datam2_i; - data[n] = datan2_r; - data[n + 1] = datan2_i; - - n -= nnstep; - m -= nnstep; - data[m] = datam1_r; - data[m + 1] = datam1_i; - data[n] = datan1_r; - data[n + 1] = datan1_i; - - } - - i += jmax << 1; - - } - - } - - // Perform Factor-4 Decomposition with 3 * complex operators and 8 +/- - // complex operators - private final static void calcF4IE(int fftFrameSize, double[] data, int i, - int nstep, double[] w) { - final int fftFrameSize2 = fftFrameSize << 1; // 2*fftFrameSize; - // Factor-4 Decomposition - - int w_len = w.length >> 1; - while (nstep < fftFrameSize2) { - - int jmax = nstep; - int nnstep = nstep << 1; - if (nnstep == fftFrameSize2) { - // Factor-4 Decomposition not possible - calcF2E(fftFrameSize, data, i, nstep, w); - return; - } - nstep <<= 2; - int ii = i + jmax; - int iii = i + w_len; - for (int n = 0; n < jmax; n += 2) { - double wr = w[i++]; - double wi = w[i++]; - double wr1 = w[ii++]; - double wi1 = w[ii++]; - double wwr1 = w[iii++]; - double wwi1 = w[iii++]; - // double wwr1 = wr * wr1 - wi * wi1; // these numbers can be - // precomputed!!! - // double wwi1 = wr * wi1 + wi * wr1; - - int m = n + jmax; - - double datam1_r = data[m]; - double datam1_i = data[m + 1]; - double datan1_r = data[n]; - double datan1_i = data[n + 1]; - - n += nnstep; - m += nnstep; - double datam2_r = data[m]; - double datam2_i = data[m + 1]; - double datan2_r = data[n]; - double datan2_i = data[n + 1]; - - double tempr = datam1_r * wr - datam1_i * wi; - double tempi = datam1_r * wi + datam1_i * wr; - - datam1_r = datan1_r - tempr; - datam1_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - double n2w1r = datan2_r * wr1 - datan2_i * wi1; - double n2w1i = datan2_r * wi1 + datan2_i * wr1; - double m2ww1r = datam2_r * wwr1 - datam2_i * wwi1; - double m2ww1i = datam2_r * wwi1 + datam2_i * wwr1; - - tempr = n2w1r - m2ww1r; - tempi = n2w1i - m2ww1i; - - datam2_r = datam1_r + tempi; - datam2_i = datam1_i - tempr; - datam1_r = datam1_r - tempi; - datam1_i = datam1_i + tempr; - - tempr = n2w1r + m2ww1r; - tempi = n2w1i + m2ww1i; - - datan2_r = datan1_r - tempr; - datan2_i = datan1_i - tempi; - datan1_r = datan1_r + tempr; - datan1_i = datan1_i + tempi; - - data[m] = datam2_r; - data[m + 1] = datam2_i; - data[n] = datan2_r; - data[n + 1] = datan2_i; - - n -= nnstep; - m -= nnstep; - data[m] = datam1_r; - data[m + 1] = datam1_i; - data[n] = datan1_r; - data[n + 1] = datan1_i; - - } - - i += jmax << 1; - - } - - } - - private final void bitreversal(double[] data) { - if (fftFrameSize < 4) - return; - - int inverse = fftFrameSize2 - 2; - for (int i = 0; i < fftFrameSize; i += 4) { - int j = bitm_array[i]; - - // Performing Bit-Reversal, even v.s. even, O(2N) - if (i < j) { - - int n = i; - int m = j; - - // COMPLEX: SWAP(data[n], data[m]) - // Real Part - double tempr = data[n]; - data[n] = data[m]; - data[m] = tempr; - // Imagery Part - n++; - m++; - double tempi = data[n]; - data[n] = data[m]; - data[m] = tempi; - - n = inverse - i; - m = inverse - j; - - // COMPLEX: SWAP(data[n], data[m]) - // Real Part - tempr = data[n]; - data[n] = data[m]; - data[m] = tempr; - // Imagery Part - n++; - m++; - tempi = data[n]; - data[n] = data[m]; - data[m] = tempi; - } - - // Performing Bit-Reversal, odd v.s. even, O(N) - - int m = j + fftFrameSize; // bitm_array[i+2]; - // COMPLEX: SWAP(data[n], data[m]) - // Real Part - int n = i + 2; - double tempr = data[n]; - data[n] = data[m]; - data[m] = tempr; - // Imagery Part - n++; - m++; - double tempi = data[n]; - data[n] = data[m]; - data[m] = tempi; - } - - } -} \ No newline at end of file