yonge 3 년 전
부모
커밋
c314c42103

+ 1084 - 0
audio-analysis/src/main/java/com/yonge/audio/analysis/AudioFloatConverter.java

@@ -0,0 +1,1084 @@
+/*
+*      _______                       _____   _____ _____  
+*     |__   __|                     |  __ \ / ____|  __ \ 
+*        | | __ _ _ __ ___  ___  ___| |  | | (___ | |__) |
+*        | |/ _` | '__/ __|/ _ \/ __| |  | |\___ \|  ___/ 
+*        | | (_| | |  \__ \ (_) \__ \ |__| |____) | |     
+*        |_|\__,_|_|  |___/\___/|___/_____/|_____/|_|     
+*                                                         
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*  
+* -------------------------------------------------------------
+*
+*  Info: http://0110.be/tag/TarsosDSP
+*  Github: https://github.com/JorenSix/TarsosDSP
+*  Releases: http://0110.be/releases/TarsosDSP/
+*  
+*  TarsosDSP includes modified source code by various authors,
+*  for credits and info, see README.
+* 
+*/
+
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package com.yonge.audio.analysis;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioFormat.Encoding;
+
+
+/**
+ * This class is used to convert between 8,16,24,32,32+ bit signed/unsigned
+ * big/litle endian fixed/floating point byte buffers and float buffers.
+ * 
+ * @author Karl Helgason
+ */
+public abstract class AudioFloatConverter {
+
+    public static final Encoding PCM_FLOAT = new Encoding("PCM_FLOAT");
+    
+    /***************************************************************************
+     * 
+     * LSB Filter, used filter least significant byte in samples arrays.
+     * 
+     * Is used filter out data in lsb byte when SampleSizeInBits is not
+     * dividable by 8.
+     * 
+     **************************************************************************/
+
+    private static class AudioFloatLSBFilter extends AudioFloatConverter {
+
+        private AudioFloatConverter converter;
+
+        final private int offset;
+
+        final private int stepsize;
+
+        final private byte mask;
+
+        private byte[] mask_buffer;
+
+        public AudioFloatLSBFilter(AudioFloatConverter converter,
+        		AudioFormat format) {
+            int bits = format.getSampleSizeInBits();
+            boolean bigEndian = format.isBigEndian();
+            this.converter = converter;
+            stepsize = (bits + 7) / 8;
+            offset = bigEndian ? (stepsize - 1) : 0;
+            int lsb_bits = bits % 8;
+            if (lsb_bits == 0)
+                mask = (byte) 0x00;
+            else if (lsb_bits == 1)
+                mask = (byte) 0x80;
+            else if (lsb_bits == 2)
+                mask = (byte) 0xC0;
+            else if (lsb_bits == 3)
+                mask = (byte) 0xE0;
+            else if (lsb_bits == 4)
+                mask = (byte) 0xF0;
+            else if (lsb_bits == 5)
+                mask = (byte) 0xF8;
+            else if (lsb_bits == 6)
+                mask = (byte) 0xFC;
+            else if (lsb_bits == 7)
+                mask = (byte) 0xFE;
+            else
+                mask = (byte) 0xFF;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            byte[] ret = converter.toByteArray(in_buff, in_offset, in_len,
+                    out_buff, out_offset);
+
+            int out_offset_end = in_len * stepsize;
+            for (int i = out_offset + offset; i < out_offset_end; i += stepsize) {
+                out_buff[i] = (byte) (out_buff[i] & mask);
+            }
+
+            return ret;
+        }
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            if (mask_buffer == null || mask_buffer.length < in_buff.length)
+                mask_buffer = new byte[in_buff.length];
+            System.arraycopy(in_buff, 0, mask_buffer, 0, in_buff.length);
+            int in_offset_end = out_len * stepsize;
+            for (int i = in_offset + offset; i < in_offset_end; i += stepsize) {
+                mask_buffer[i] = (byte) (mask_buffer[i] & mask);
+            }
+            float[] ret = converter.toFloatArray(mask_buffer, in_offset,
+                    out_buff, out_offset, out_len);
+            return ret;
+        }
+
+    }
+
+    /***************************************************************************
+     * 
+     * 64 bit float, little/big-endian
+     * 
+     **************************************************************************/
+
+    // PCM 64 bit float, little-endian
+    private static class AudioFloatConversion64L extends AudioFloatConverter {
+        ByteBuffer bytebuffer = null;
+
+        DoubleBuffer floatbuffer = null;
+
+        double[] double_buff = null;
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int in_len = out_len * 8;
+            if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+                bytebuffer = ByteBuffer.allocate(in_len).order(
+                        ByteOrder.LITTLE_ENDIAN);
+                floatbuffer = bytebuffer.asDoubleBuffer();
+            }
+            bytebuffer.position(0);
+            floatbuffer.position(0);
+            bytebuffer.put(in_buff, in_offset, in_len);
+            if (double_buff == null
+                    || double_buff.length < out_len + out_offset)
+                double_buff = new double[out_len + out_offset];
+            floatbuffer.get(double_buff, out_offset, out_len);
+            int out_offset_end = out_offset + out_len;
+            for (int i = out_offset; i < out_offset_end; i++) {
+                out_buff[i] = (float) double_buff[i];
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int out_len = in_len * 8;
+            if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+                bytebuffer = ByteBuffer.allocate(out_len).order(
+                        ByteOrder.LITTLE_ENDIAN);
+                floatbuffer = bytebuffer.asDoubleBuffer();
+            }
+            floatbuffer.position(0);
+            bytebuffer.position(0);
+            if (double_buff == null || double_buff.length < in_offset + in_len)
+                double_buff = new double[in_offset + in_len];
+            int in_offset_end = in_offset + in_len;
+            for (int i = in_offset; i < in_offset_end; i++) {
+                double_buff[i] = in_buff[i];
+            }
+            floatbuffer.put(double_buff, in_offset, in_len);
+            bytebuffer.get(out_buff, out_offset, out_len);
+            return out_buff;
+        }
+    }
+
+    // PCM 64 bit float, big-endian
+    private static class AudioFloatConversion64B extends AudioFloatConverter {
+        ByteBuffer bytebuffer = null;
+
+        DoubleBuffer floatbuffer = null;
+
+        double[] double_buff = null;
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int in_len = out_len * 8;
+            if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+                bytebuffer = ByteBuffer.allocate(in_len).order(
+                        ByteOrder.BIG_ENDIAN);
+                floatbuffer = bytebuffer.asDoubleBuffer();
+            }
+            bytebuffer.position(0);
+            floatbuffer.position(0);
+            bytebuffer.put(in_buff, in_offset, in_len);
+            if (double_buff == null
+                    || double_buff.length < out_len + out_offset)
+                double_buff = new double[out_len + out_offset];
+            floatbuffer.get(double_buff, out_offset, out_len);
+            int out_offset_end = out_offset + out_len;
+            for (int i = out_offset; i < out_offset_end; i++) {
+                out_buff[i] = (float) double_buff[i];
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int out_len = in_len * 8;
+            if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+                bytebuffer = ByteBuffer.allocate(out_len).order(
+                        ByteOrder.BIG_ENDIAN);
+                floatbuffer = bytebuffer.asDoubleBuffer();
+            }
+            floatbuffer.position(0);
+            bytebuffer.position(0);
+            if (double_buff == null || double_buff.length < in_offset + in_len)
+                double_buff = new double[in_offset + in_len];
+            int in_offset_end = in_offset + in_len;
+            for (int i = in_offset; i < in_offset_end; i++) {
+                double_buff[i] = in_buff[i];
+            }
+            floatbuffer.put(double_buff, in_offset, in_len);
+            bytebuffer.get(out_buff, out_offset, out_len);
+            return out_buff;
+        }
+    }
+
+    /***************************************************************************
+     * 
+     * 32 bit float, little/big-endian
+     * 
+     **************************************************************************/
+
+    // PCM 32 bit float, little-endian
+    private static class AudioFloatConversion32L extends AudioFloatConverter {
+        ByteBuffer bytebuffer = null;
+
+        FloatBuffer floatbuffer = null;
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int in_len = out_len * 4;
+            if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+                bytebuffer = ByteBuffer.allocate(in_len).order(
+                        ByteOrder.LITTLE_ENDIAN);
+                floatbuffer = bytebuffer.asFloatBuffer();
+            }
+            bytebuffer.position(0);
+            floatbuffer.position(0);
+            bytebuffer.put(in_buff, in_offset, in_len);
+            floatbuffer.get(out_buff, out_offset, out_len);
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int out_len = in_len * 4;
+            if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+                bytebuffer = ByteBuffer.allocate(out_len).order(
+                        ByteOrder.LITTLE_ENDIAN);
+                floatbuffer = bytebuffer.asFloatBuffer();
+            }
+            floatbuffer.position(0);
+            bytebuffer.position(0);
+            floatbuffer.put(in_buff, in_offset, in_len);
+            bytebuffer.get(out_buff, out_offset, out_len);
+            return out_buff;
+        }
+    }
+
+    // PCM 32 bit float, big-endian
+    private static class AudioFloatConversion32B extends AudioFloatConverter {
+        ByteBuffer bytebuffer = null;
+
+        FloatBuffer floatbuffer = null;
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int in_len = out_len * 4;
+            if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+                bytebuffer = ByteBuffer.allocate(in_len).order(
+                        ByteOrder.BIG_ENDIAN);
+                floatbuffer = bytebuffer.asFloatBuffer();
+            }
+            bytebuffer.position(0);
+            floatbuffer.position(0);
+            bytebuffer.put(in_buff, in_offset, in_len);
+            floatbuffer.get(out_buff, out_offset, out_len);
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int out_len = in_len * 4;
+            if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+                bytebuffer = ByteBuffer.allocate(out_len).order(
+                        ByteOrder.BIG_ENDIAN);
+                floatbuffer = bytebuffer.asFloatBuffer();
+            }
+            floatbuffer.position(0);
+            bytebuffer.position(0);
+            floatbuffer.put(in_buff, in_offset, in_len);
+            bytebuffer.get(out_buff, out_offset, out_len);
+            return out_buff;
+        }
+    }
+
+    /***************************************************************************
+     * 
+     * 8 bit signed/unsigned
+     * 
+     **************************************************************************/
+
+    // PCM 8 bit, signed
+    private static class AudioFloatConversion8S extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++)
+                out_buff[ox++] = in_buff[ix++] * (1.0f / 127.0f);
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++)
+                out_buff[ox++] = (byte) (in_buff[ix++] * 127.0f);
+            return out_buff;
+        }
+    }
+
+    // PCM 8 bit, unsigned
+    private static class AudioFloatConversion8U extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++)
+                out_buff[ox++] = ((in_buff[ix++] & 0xFF) - 127)
+                        * (1.0f / 127.0f);
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++)
+                out_buff[ox++] = (byte) (127 + in_buff[ix++] * 127.0f);
+            return out_buff;
+        }
+    }
+
+    /***************************************************************************
+     * 
+     * 16 bit signed/unsigned, little/big-endian
+     * 
+     **************************************************************************/
+
+    // PCM 16 bit, signed, little-endian
+    private static class AudioFloatConversion16SL extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int len = out_offset + out_len;
+            for (int ox = out_offset; ox < len; ox++) {
+                out_buff[ox] = ((short) ((in_buff[ix++] & 0xFF) | 
+                           (in_buff[ix++] << 8))) * (1.0f / 32767.0f);
+            }
+
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ox = out_offset;
+            int len = in_offset + in_len;
+            for (int ix = in_offset; ix < len; ix++) {
+                int x = (int) (in_buff[ix] * 32767.0);
+                out_buff[ox++] = (byte) x;
+                out_buff[ox++] = (byte) (x >>> 8);
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 16 bit, signed, big-endian
+    private static class AudioFloatConversion16SB extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                out_buff[ox++] = ((short) ((in_buff[ix++] << 8) | 
+                        (in_buff[ix++] & 0xFF))) * (1.0f / 32767.0f);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * 32767.0);
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) x;
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 16 bit, unsigned, little-endian
+    private static class AudioFloatConversion16UL extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8);
+                out_buff[ox++] = (x - 32767) * (1.0f / 32767.0f);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = 32767 + (int) (in_buff[ix++] * 32767.0);
+                out_buff[ox++] = (byte) x;
+                out_buff[ox++] = (byte) (x >>> 8);
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 16 bit, unsigned, big-endian
+    private static class AudioFloatConversion16UB extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+                out_buff[ox++] = (x - 32767) * (1.0f / 32767.0f);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = 32767 + (int) (in_buff[ix++] * 32767.0);
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) x;
+            }
+            return out_buff;
+        }
+    }
+
+    /***************************************************************************
+     * 
+     * 24 bit signed/unsigned, little/big-endian
+     * 
+     **************************************************************************/
+
+    // PCM 24 bit, signed, little-endian
+    private static class AudioFloatConversion24SL extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+                        | ((in_buff[ix++] & 0xFF) << 16);
+                if (x > 0x7FFFFF)
+                    x -= 0x1000000;
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+                if (x < 0)
+                    x += 0x1000000;
+                out_buff[ox++] = (byte) x;
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) (x >>> 16);
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 24 bit, signed, big-endian
+    private static class AudioFloatConversion24SB extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = ((in_buff[ix++] & 0xFF) << 16)
+                        | ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+                if (x > 0x7FFFFF)
+                    x -= 0x1000000;
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+                if (x < 0)
+                    x += 0x1000000;
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) x;
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 24 bit, unsigned, little-endian
+    private static class AudioFloatConversion24UL extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+                        | ((in_buff[ix++] & 0xFF) << 16);
+                x -= 0x7FFFFF;
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+                x += 0x7FFFFF;
+                out_buff[ox++] = (byte) x;
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) (x >>> 16);
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 24 bit, unsigned, big-endian
+    private static class AudioFloatConversion24UB extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = ((in_buff[ix++] & 0xFF) << 16)
+                        | ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+                x -= 0x7FFFFF;
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+                x += 0x7FFFFF;
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) x;
+            }
+            return out_buff;
+        }
+    }
+
+    /***************************************************************************
+     * 
+     * 32 bit signed/unsigned, little/big-endian
+     * 
+     **************************************************************************/
+
+    // PCM 32 bit, signed, little-endian
+    private static class AudioFloatConversion32SL extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8) |
+                        ((in_buff[ix++] & 0xFF) << 16) |
+                        ((in_buff[ix++] & 0xFF) << 24);
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+                out_buff[ox++] = (byte) x;
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 24);
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 32 bit, signed, big-endian
+    private static class AudioFloatConversion32SB extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = ((in_buff[ix++] & 0xFF) << 24) |
+                        ((in_buff[ix++] & 0xFF) << 16) |
+                        ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+                out_buff[ox++] = (byte) (x >>> 24);
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) x;
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 32 bit, unsigned, little-endian
+    private static class AudioFloatConversion32UL extends AudioFloatConverter {
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8) |
+                        ((in_buff[ix++] & 0xFF) << 16) | 
+                        ((in_buff[ix++] & 0xFF) << 24);
+                x -= 0x7FFFFFFF;
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+                x += 0x7FFFFFFF;
+                out_buff[ox++] = (byte) x;
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 24);
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 32 bit, unsigned, big-endian
+    private static class AudioFloatConversion32UB extends AudioFloatConverter {
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = ((in_buff[ix++] & 0xFF) << 24) |
+                        ((in_buff[ix++] & 0xFF) << 16) |
+                        ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+                x -= 0x7FFFFFFF;
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+                x += 0x7FFFFFFF;
+                out_buff[ox++] = (byte) (x >>> 24);
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) x;
+            }
+            return out_buff;
+        }
+    }
+
+    /***************************************************************************
+     * 
+     * 32+ bit signed/unsigned, little/big-endian
+     * 
+     **************************************************************************/
+
+    // PCM 32+ bit, signed, little-endian
+    private static class AudioFloatConversion32xSL extends AudioFloatConverter {
+
+        final int xbytes;
+
+        public AudioFloatConversion32xSL(int xbytes) {
+            this.xbytes = xbytes;
+        }
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                ix += xbytes;
+                int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+                        | ((in_buff[ix++] & 0xFF) << 16)
+                        | ((in_buff[ix++] & 0xFF) << 24);
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+                for (int j = 0; j < xbytes; j++) {
+                    out_buff[ox++] = 0;
+                }
+                out_buff[ox++] = (byte) x;
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 24);
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 32+ bit, signed, big-endian
+    private static class AudioFloatConversion32xSB extends AudioFloatConverter {
+
+        final int xbytes;
+
+        public AudioFloatConversion32xSB(int xbytes) {
+            this.xbytes = xbytes;
+        }
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = ((in_buff[ix++] & 0xFF) << 24)
+                        | ((in_buff[ix++] & 0xFF) << 16)
+                        | ((in_buff[ix++] & 0xFF) << 8)
+                        | (in_buff[ix++] & 0xFF);
+                ix += xbytes;
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+                out_buff[ox++] = (byte) (x >>> 24);
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) x;
+                for (int j = 0; j < xbytes; j++) {
+                    out_buff[ox++] = 0;
+                }
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 32+ bit, unsigned, little-endian
+    private static class AudioFloatConversion32xUL extends AudioFloatConverter {
+
+        final int xbytes;
+
+        public AudioFloatConversion32xUL(int xbytes) {
+            this.xbytes = xbytes;
+        }
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                ix += xbytes;
+                int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+                        | ((in_buff[ix++] & 0xFF) << 16)
+                        | ((in_buff[ix++] & 0xFF) << 24);
+                x -= 0x7FFFFFFF;
+                out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+                x += 0x7FFFFFFF;
+                for (int j = 0; j < xbytes; j++) {
+                    out_buff[ox++] = 0;
+                }
+                out_buff[ox++] = (byte) x;
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 24);
+            }
+            return out_buff;
+        }
+    }
+
+    // PCM 32+ bit, unsigned, big-endian
+    private static class AudioFloatConversion32xUB extends AudioFloatConverter {
+
+        final int xbytes;
+
+        public AudioFloatConversion32xUB(int xbytes) {
+            this.xbytes = xbytes;
+        }
+
+        public float[] toFloatArray(byte[] in_buff, int in_offset,
+                float[] out_buff, int out_offset, int out_len) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < out_len; i++) {
+                int x = ((in_buff[ix++] & 0xFF) << 24) |
+                        ((in_buff[ix++] & 0xFF) << 16) |
+                        ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+                ix += xbytes;
+                x -= 2147483647;
+                out_buff[ox++] = x * (1.0f / 2147483647.0f);
+            }
+            return out_buff;
+        }
+
+        public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+                byte[] out_buff, int out_offset) {
+            int ix = in_offset;
+            int ox = out_offset;
+            for (int i = 0; i < in_len; i++) {
+                int x = (int) (in_buff[ix++] * 2147483647.0);
+                x += 2147483647;
+                out_buff[ox++] = (byte) (x >>> 24);
+                out_buff[ox++] = (byte) (x >>> 16);
+                out_buff[ox++] = (byte) (x >>> 8);
+                out_buff[ox++] = (byte) x;
+                for (int j = 0; j < xbytes; j++) {
+                    out_buff[ox++] = 0;
+                }
+            }
+            return out_buff;
+        }
+    }
+
+    public static AudioFloatConverter getConverter(AudioFormat format) {
+    	AudioFloatConverter conv = null;
+        if (format.getFrameSize() == 0)
+            return null;
+        if (format.getFrameSize() != 
+                ((format.getSampleSizeInBits() + 7) / 8) * format.getChannels()) {
+            return null;
+        }
+        if (format.getEncoding().equals(Encoding.PCM_SIGNED)) {
+            if (format.isBigEndian()) {
+                if (format.getSampleSizeInBits() <= 8) {
+                    conv = new AudioFloatConversion8S();
+                } else if (format.getSampleSizeInBits() > 8 &&
+                      format.getSampleSizeInBits() <= 16) {
+                    conv = new AudioFloatConversion16SB();
+                } else if (format.getSampleSizeInBits() > 16 &&
+                      format.getSampleSizeInBits() <= 24) {
+                    conv = new AudioFloatConversion24SB();
+                } else if (format.getSampleSizeInBits() > 24 &&
+                      format.getSampleSizeInBits() <= 32) {
+                    conv = new AudioFloatConversion32SB();
+                } else if (format.getSampleSizeInBits() > 32) {
+                    conv = new AudioFloatConversion32xSB(((format
+                            .getSampleSizeInBits() + 7) / 8) - 4);
+                } 
+            } else {
+                if (format.getSampleSizeInBits() <= 8) {
+                    conv = new AudioFloatConversion8S();
+                } else if (format.getSampleSizeInBits() > 8 &&
+                         format.getSampleSizeInBits() <= 16) {
+                    conv = new AudioFloatConversion16SL();
+                } else if (format.getSampleSizeInBits() > 16 &&
+                         format.getSampleSizeInBits() <= 24) {
+                    conv = new AudioFloatConversion24SL();
+                } else if (format.getSampleSizeInBits() > 24 &&
+                         format.getSampleSizeInBits() <= 32) {
+                    conv = new AudioFloatConversion32SL();
+                } else if (format.getSampleSizeInBits() > 32) {
+                    conv = new AudioFloatConversion32xSL(((format
+                            .getSampleSizeInBits() + 7) / 8) - 4);
+                }
+            }
+        } else if (format.getEncoding().equals(Encoding.PCM_UNSIGNED)) {
+            if (format.isBigEndian()) {
+                if (format.getSampleSizeInBits() <= 8) {
+                    conv = new AudioFloatConversion8U();
+                } else if (format.getSampleSizeInBits() > 8 &&
+                        format.getSampleSizeInBits() <= 16) {
+                    conv = new AudioFloatConversion16UB();
+                } else if (format.getSampleSizeInBits() > 16 &&
+                        format.getSampleSizeInBits() <= 24) {
+                    conv = new AudioFloatConversion24UB();
+                } else if (format.getSampleSizeInBits() > 24 &&
+                        format.getSampleSizeInBits() <= 32) {
+                    conv = new AudioFloatConversion32UB();
+                } else if (format.getSampleSizeInBits() > 32) {
+                    conv = new AudioFloatConversion32xUB(((
+                            format.getSampleSizeInBits() + 7) / 8) - 4);
+                }
+            } else {
+                if (format.getSampleSizeInBits() <= 8) {
+                    conv = new AudioFloatConversion8U();
+                } else if (format.getSampleSizeInBits() > 8 &&
+                        format.getSampleSizeInBits() <= 16) {
+                    conv = new AudioFloatConversion16UL();
+                } else if (format.getSampleSizeInBits() > 16 &&
+                        format.getSampleSizeInBits() <= 24) {
+                    conv = new AudioFloatConversion24UL();
+                } else if (format.getSampleSizeInBits() > 24 &&
+                        format.getSampleSizeInBits() <= 32) {
+                    conv = new AudioFloatConversion32UL();
+                } else if (format.getSampleSizeInBits() > 32) {
+                    conv = new AudioFloatConversion32xUL(((
+                            format.getSampleSizeInBits() + 7) / 8) - 4);
+                }
+            }
+        } else if (format.getEncoding().equals(PCM_FLOAT)) {
+            if (format.getSampleSizeInBits() == 32) {
+                if (format.isBigEndian())
+                    conv = new AudioFloatConversion32B();
+                else
+                    conv = new AudioFloatConversion32L();
+            } else if (format.getSampleSizeInBits() == 64) {
+                if (format.isBigEndian()) 
+                    conv = new AudioFloatConversion64B();
+                else 
+                    conv = new AudioFloatConversion64L();                
+            }
+
+        }
+
+        if ((format.getEncoding().equals(Encoding.PCM_SIGNED) || 
+                format.getEncoding().equals(Encoding.PCM_UNSIGNED)) && 
+                (format.getSampleSizeInBits() % 8 != 0)) {
+            conv = new AudioFloatLSBFilter(conv, format);
+        }
+
+        if (conv != null)
+            conv.format = format;
+        return conv;
+    }
+
+    private AudioFormat format;
+
+    public AudioFormat getFormat() {
+        return format;
+    }
+
+    public abstract float[] toFloatArray(byte[] in_buff, int in_offset,
+            float[] out_buff, int out_offset, int out_len);
+
+    public float[] toFloatArray(byte[] in_buff, float[] out_buff,
+            int out_offset, int out_len) {
+        return toFloatArray(in_buff, 0, out_buff, out_offset, out_len);
+    }
+
+    public float[] toFloatArray(byte[] in_buff, int in_offset,
+            float[] out_buff, int out_len) {
+        return toFloatArray(in_buff, in_offset, out_buff, 0, out_len);
+    }
+
+    public float[] toFloatArray(byte[] in_buff, float[] out_buff, int out_len) {
+        return toFloatArray(in_buff, 0, out_buff, 0, out_len);
+    }
+
+    public float[] toFloatArray(byte[] in_buff, float[] out_buff) {
+        return toFloatArray(in_buff, 0, out_buff, 0, out_buff.length);
+    }
+
+    public abstract byte[] toByteArray(float[] in_buff, int in_offset,
+            int in_len, byte[] out_buff, int out_offset);
+
+    public byte[] toByteArray(float[] in_buff, int in_len, byte[] out_buff,
+            int out_offset) {
+        return toByteArray(in_buff, 0, in_len, out_buff, out_offset);
+    }
+
+    public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+            byte[] out_buff) {
+        return toByteArray(in_buff, in_offset, in_len, out_buff, 0);
+    }
+
+    public byte[] toByteArray(float[] in_buff, int in_len, byte[] out_buff) {
+        return toByteArray(in_buff, 0, in_len, out_buff, 0);
+    }
+
+    public byte[] toByteArray(float[] in_buff, byte[] out_buff) {
+        return toByteArray(in_buff, 0, in_buff.length, out_buff, 0);
+    }
+
+}

+ 158 - 0
audio-analysis/src/main/java/com/yonge/audio/analysis/Complex.java

@@ -0,0 +1,158 @@
+package com.yonge.audio.analysis;
+
+/*************************************************************************
+ * Compilation: javac Complex.java Execution: java Complex
+ * 
+ * Data type for complex numbers.
+ * 
+ * The data type is "immutable" so once you create and initialize a Complex
+ * object, you cannot change it. The "final" keyword when declaring re and im
+ * enforces this rule, making it a compile-time error to change the .re or .im
+ * fields after they've been initialized.
+ * 
+ * % java Complex a = 5.0 + 6.0i b = -3.0 + 4.0i Re(a) = 5.0 Im(a) = 6.0 b + a =
+ * 2.0 + 10.0i a - b = 8.0 + 2.0i a * b = -39.0 + 2.0i b * a = -39.0 + 2.0i a /
+ * b = 0.36 - 1.52i (a / b) * b = 5.0 + 6.0i conj(a) = 5.0 - 6.0i |a| =
+ * 7.810249675906654 tan(a) = -6.685231390246571E-6 + 1.0000103108981198i
+ * 
+ *************************************************************************/
+
+public class Complex {
+	private final double re; // the real part
+	private final double im; // the imaginary part
+
+	// create a new object with the given real and imaginary parts
+	public Complex(double real, double imag) {
+		re = real;
+		im = imag;
+	}
+
+	// return a string representation of the invoking Complex object
+	public String toString() {
+		if (im == 0)
+			return re + "";
+		if (re == 0)
+			return im + "i";
+		if (im < 0)
+			return re + " - " + (-im) + "i";
+		return re + " + " + im + "i";
+	}
+
+	// return abs/modulus/magnitude and angle/phase/argument
+	public double abs() {
+		return Math.hypot(re, im);
+	} // Math.sqrt(re*re + im*im)
+
+	public double phase() {
+		return Math.atan2(im, re);
+	} // between -pi and pi
+
+	// return a new Complex object whose value is (this + b)
+	public Complex plus(Complex b) {
+		Complex a = this; // invoking object
+		double real = a.re + b.re;
+		double imag = a.im + b.im;
+		return new Complex(real, imag);
+	}
+
+	// return a new Complex object whose value is (this - b)
+	public Complex minus(Complex b) {
+		Complex a = this;
+		double real = a.re - b.re;
+		double imag = a.im - b.im;
+		return new Complex(real, imag);
+	}
+
+	// return a new Complex object whose value is (this * b)
+	public Complex times(Complex b) {
+		Complex a = this;
+		double real = a.re * b.re - a.im * b.im;
+		double imag = a.re * b.im + a.im * b.re;
+		return new Complex(real, imag);
+	}
+
+	// scalar multiplication
+	// return a new object whose value is (this * alpha)
+	public Complex times(double alpha) {
+		return new Complex(alpha * re, alpha * im);
+	}
+
+	// return a new Complex object whose value is the conjugate of this
+	public Complex conjugate() {
+		return new Complex(re, -im);
+	}
+
+	// return a new Complex object whose value is the reciprocal of this
+	public Complex reciprocal() {
+		double scale = re * re + im * im;
+		return new Complex(re / scale, -im / scale);
+	}
+
+	// return the real or imaginary part
+	public double re() {
+		return re;
+	}
+
+	public double im() {
+		return im;
+	}
+
+	// return a / b
+	public Complex divides(Complex b) {
+		Complex a = this;
+		return a.times(b.reciprocal());
+	}
+
+	// return a new Complex object whose value is the complex exponential of
+	// this
+	public Complex exp() {
+		return new Complex(Math.exp(re) * Math.cos(im), Math.exp(re)
+				* Math.sin(im));
+	}
+
+	// return a new Complex object whose value is the complex sine of this
+	public Complex sin() {
+		return new Complex(Math.sin(re) * Math.cosh(im), Math.cos(re)
+				* Math.sinh(im));
+	}
+
+	// return a new Complex object whose value is the complex cosine of this
+	public Complex cos() {
+		return new Complex(Math.cos(re) * Math.cosh(im), -Math.sin(re)
+				* Math.sinh(im));
+	}
+
+	// return a new Complex object whose value is the complex tangent of this
+	public Complex tan() {
+		return sin().divides(cos());
+	}
+
+	// a static version of plus
+	public static Complex plus(Complex a, Complex b) {
+		double real = a.re + b.re;
+		double imag = a.im + b.im;
+		Complex sum = new Complex(real, imag);
+		return sum;
+	}
+
+	
+	public static void main(String[] args) {
+		Complex a = new Complex(5.0, 0.0);
+		Complex b = new Complex(-3.0, 4.0);
+
+		System.out.println("a            = " + a);
+		System.out.println("b            = " + b);
+		System.out.println("Re(a)        = " + a.re());
+		System.out.println("Im(a)        = " + a.im());
+		System.out.println("b + a        = " + b.plus(a));
+		System.out.println("a - b        = " + a.minus(b));
+		System.out.println("a * b        = " + a.times(b));
+		System.out.println("b * a        = " + b.times(a));
+		System.out.println("a / b        = " + a.divides(b));
+		System.out.println("(a / b) * b  = " + a.divides(b).times(b));
+		System.out.println("conj(a)      = " + a.conjugate());
+		System.out.println("|a|          = " + a.abs());
+		System.out.println("tan(a)       = " + a.tan());
+	}
+
+}

+ 167 - 0
audio-analysis/src/main/java/com/yonge/audio/analysis/FFT.java

@@ -0,0 +1,167 @@
+package com.yonge.audio.analysis;
+
+
+/*************************************************************************
+ * Compilation: javac FFT.java Execution: java FFT N Dependencies: Complex.java
+ * 
+ * Compute the FFT and inverse FFT of a length N complex sequence. Bare bones
+ * implementation that runs in O(N log N) time. Our goal is to optimize the
+ * clarity of the code, rather than performance.
+ * 
+ * Limitations ----------- - assumes N is a power of 2
+ * 
+ * - not the most memory efficient algorithm (because it uses an object type for
+ * representing complex numbers and because it re-allocates memory for the
+ * subarray, instead of doing in-place or reusing a single temporary array)
+ * 
+ *************************************************************************/
+
+public class FFT {
+
+	// compute the FFT of x[], assuming its length is a power of 2
+	public static Complex[] fft(Complex[] x) {
+		int N = x.length;
+
+		// base case
+		if (N == 1)
+			return new Complex[] { x[0] };
+
+		// radix 2 Cooley-Tukey FFT
+		if (N % 2 != 0) {
+			throw new RuntimeException("N is not a power of 2");
+		}
+
+		// fft of even terms
+		Complex[] even = new Complex[N / 2];
+		for (int k = 0; k < N / 2; k++) {
+			even[k] = x[2 * k];
+		}
+		Complex[] q = fft(even);
+
+		// fft of odd terms
+		Complex[] odd = even; // reuse the array
+		for (int k = 0; k < N / 2; k++) {
+			odd[k] = x[2 * k + 1];
+		}
+		Complex[] r = fft(odd);
+
+		// combine
+		Complex[] y = new Complex[N];
+		for (int k = 0; k < N / 2; k++) {
+			double kth = -2 * k * Math.PI / N;
+			Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
+			y[k] = q[k].plus(wk.times(r[k]));
+			y[k + N / 2] = q[k].minus(wk.times(r[k]));
+		}
+		return y;
+	}
+
+	// compute the inverse FFT of x[], assuming its length is a power of 2
+	public static Complex[] ifft(Complex[] x) {
+		int N = x.length;
+		Complex[] y = new Complex[N];
+
+		// take conjugate
+		for (int i = 0; i < N; i++) {
+			y[i] = x[i].conjugate();
+		}
+
+		// compute forward FFT
+		y = fft(y);
+
+		// take conjugate again
+		for (int i = 0; i < N; i++) {
+			y[i] = y[i].conjugate();
+		}
+
+		// divide by N
+		for (int i = 0; i < N; i++) {
+			y[i] = y[i].times(1.0 / N);
+		}
+
+		return y;
+
+	}
+
+	// compute the circular convolution of x and y
+	public static Complex[] cconvolve(Complex[] x, Complex[] y) {
+
+		// should probably pad x and y with 0s so that they have same length
+		// and are powers of 2
+		if (x.length != y.length) {
+			throw new RuntimeException("Dimensions don't agree");
+		}
+
+		int N = x.length;
+
+		// compute FFT of each sequence
+		Complex[] a = fft(x);
+		Complex[] b = fft(y);
+
+		// point-wise multiply
+		Complex[] c = new Complex[N];
+		for (int i = 0; i < N; i++) {
+			c[i] = a[i].times(b[i]);
+		}
+
+		// compute inverse FFT
+		return ifft(c);
+	}
+
+	// compute the linear convolution of x and y
+	public static Complex[] convolve(Complex[] x, Complex[] y) {
+		Complex ZERO = new Complex(0, 0);
+
+		Complex[] a = new Complex[2 * x.length];
+		for (int i = 0; i < x.length; i++)
+			a[i] = x[i];
+		for (int i = x.length; i < 2 * x.length; i++)
+			a[i] = ZERO;
+
+		Complex[] b = new Complex[2 * y.length];
+		for (int i = 0; i < y.length; i++)
+			b[i] = y[i];
+		for (int i = y.length; i < 2 * y.length; i++)
+			b[i] = ZERO;
+
+		return cconvolve(a, b);
+	}
+
+	// display an array of Complex numbers to standard output
+	public static void show(Complex[] x, String title) {
+		System.out.println(title);
+		System.out.println("-------------------");
+		for (int i = 0; i < x.length; i++) {
+			System.out.println(x[i]);
+		}
+		System.out.println();
+	}
+
+	/*********************************************************************
+	 * Test client and sample execution
+	 * 
+	 * % java FFT 4 x ------------------- -0.03480425839330703
+	 * 0.07910192950176387 0.7233322451735928 0.1659819820667019
+	 * 
+	 * y = fft(x) ------------------- 0.9336118983487516 -0.7581365035668999 +
+	 * 0.08688005256493803i 0.44344407521182005 -0.7581365035668999 -
+	 * 0.08688005256493803i
+	 * 
+	 * z = ifft(y) ------------------- -0.03480425839330703 0.07910192950176387
+	 * + 2.6599344570851287E-18i 0.7233322451735928 0.1659819820667019 -
+	 * 2.6599344570851287E-18i
+	 * 
+	 * c = cconvolve(x, x) ------------------- 0.5506798633981853
+	 * 0.23461407150576394 - 4.033186818023279E-18i -0.016542951108772352
+	 * 0.10288019294318276 + 4.033186818023279E-18i
+	 * 
+	 * d = convolve(x, x) ------------------- 0.001211336402308083 -
+	 * 3.122502256758253E-17i -0.005506167987577068 - 5.058885073636224E-17i
+	 * -0.044092969479563274 + 2.1934338938072244E-18i 0.10288019294318276 -
+	 * 3.6147323062478115E-17i 0.5494685269958772 + 3.122502256758253E-17i
+	 * 0.240120239493341 + 4.655566391833896E-17i 0.02755001837079092 -
+	 * 2.1934338938072244E-18i 4.01805098805014E-17i
+	 * 
+	 *********************************************************************/
+
+}

+ 144 - 0
audio-analysis/src/main/java/com/yonge/audio/analysis/Signals.java

@@ -0,0 +1,144 @@
+package com.yonge.audio.analysis;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.TargetDataLine;
+
+public class Signals 
+{
+	public static float mean( float[] signal )
+	{
+		float mean = 0;
+		for( int i = 0; i < signal.length; i++ )
+			mean+=signal[i];
+		mean /= signal.length;
+		return mean;
+	}
+	
+	public static float energy( float[] signal )
+	{
+		float totalEnergy = 0;
+		for( int i = 0; i < signal.length; i++ )		
+			totalEnergy += Math.pow(signal[i], 2);
+		return totalEnergy;
+	}
+
+	public static float power(float[] signal ) 
+	{	
+		return energy( signal ) / signal.length;
+	}
+	
+	public static float norm( float[] signal )
+	{
+		return (float)Math.sqrt( energy(signal) );
+	}
+	
+	public static float minimum( float[] signal )
+	{
+		float min = Float.POSITIVE_INFINITY;
+		for( int i = 0; i < signal.length; i++ )
+			min = Math.min( min, signal[i] );
+		return min;
+	}
+	
+	public static float maximum( float[] signal )
+	{
+		float max = Float.NEGATIVE_INFINITY;
+		for( int i = 0; i < signal.length; i++ )
+			max = Math.max( max, signal[i] );
+		return max;
+	}
+	
+	public static void scale( float[] signal, float scale )
+	{
+		for( int i = 0; i < signal.length; i++ )
+			signal[i] *= scale;
+	}
+	
+	public static float rms(float[] samples) {
+		// 均方根 (RMS) 功率
+		return (float) Math.sqrt(power(samples));
+
+	}
+
+	public static double soundPressureLevel(float[] samples) {
+
+		double rms = rms(samples);
+		// 计算声强级(Sound Pressure Level)
+		return (20.0 * Math.log10(rms));
+	}
+	
+	public static int decibels(float[] samples) {
+		// 声音的分贝范围
+		double minDecibels = 0, db = 0, maxDecibels = 127;
+
+		double rms = rms(samples);
+
+		if (rms > 0) {
+			db = 20 * Math.log10(rms / 0.00002);// 空气中常用的“零”参考声压为20 uPa RMS,通常被认为是人类听力的阈值
+
+			if (db > maxDecibels) {
+				db = maxDecibels;
+			} else if (db < minDecibels) {
+				db = minDecibels;
+			}
+		}
+
+		return (int) db;
+	}
+	
+	public static void main(String[] args) throws LineUnavailableException {
+		
+		float sampleRate = 44100;
+		
+		AudioFormat audioFormat = new AudioFormat(sampleRate, 16, 1, true, false);
+		
+		DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
+		
+		TargetDataLine targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
+		
+		targetDataLine.open(audioFormat);
+        targetDataLine.start();
+        
+        AudioFloatConverter converter = AudioFloatConverter.getConverter(audioFormat);
+        
+        byte[] buffer = new byte[1024 * 8];
+
+        while(true){
+        	targetDataLine.read(buffer, 0, buffer.length);
+        	
+        	float[] sampleFloats = new float[buffer.length /2];
+        	converter.toFloatArray(buffer,sampleFloats);
+        	
+        	
+        	//计算声强级(Sound Pressure Level)
+        	double splDb = soundPressureLevel(sampleFloats);
+        	
+        	int db = decibels(sampleFloats);
+        	
+        	
+        	Complex[] complex = new Complex[sampleFloats.length];
+        	
+        	for(int i = 0;i<sampleFloats.length;i++){
+        		complex[i] = new Complex(sampleFloats[i], 0);
+        	}
+        	Complex[] result = FFT.fft(complex);
+        	
+        	double maxMagnitude = result[0].abs();
+        	int maxIndex = 0;
+        	
+        	for(int i = 1;i<result.length / 2;i++){
+        		if(maxMagnitude < result[i].abs()){
+        			maxMagnitude = result[i].abs();
+        			maxIndex = i;
+        		}
+        	}
+        	
+        	double f = maxIndex * sampleRate / result.length;
+        	
+        	System.out.println("db:" + db + "	power:" + power(sampleFloats) + "	splDb: " + splDb + "	frequency: " + f);
+        }
+	}
+}

+ 46 - 0
audio-analysis/src/main/java/com/yonge/audio/analysis/detector/FrequencyDetector.java

@@ -0,0 +1,46 @@
+package com.yonge.audio.analysis.detector;
+
+import com.yonge.audio.analysis.Complex;
+import com.yonge.audio.analysis.FFT;
+
+public class FrequencyDetector {
+
+	private float[] samples;
+
+	private boolean isUseHanmingWindow;
+
+	private float sampleRate;
+
+	public FrequencyDetector(float[] samples, float sampleRate, boolean isUseHanmingWindow) {
+		this.samples = samples;
+		this.sampleRate = sampleRate;
+		this.isUseHanmingWindow = isUseHanmingWindow;
+	}
+
+	public double getFrequency() {
+
+		if (isUseHanmingWindow) {
+			// 加汉明窗
+			// hamming(samples);
+		}
+
+		Complex[] complex = new Complex[samples.length];
+
+		for (int i = 0; i < samples.length; i++) {
+			complex[i] = new Complex(samples[i], 0);
+		}
+		Complex[] result = FFT.fft(complex);
+
+		double maxMagnitude = result[0].abs();
+		int maxIndex = 0;
+
+		for (int i = 1; i < result.length / 2; i++) {
+			if (maxMagnitude < result[i].abs()) {
+				maxMagnitude = result[i].abs();
+				maxIndex = i;
+			}
+		}
+
+		return maxIndex * sampleRate / result.length;
+	}
+}

+ 73 - 0
audio-analysis/src/main/java/com/yonge/nettty/dto/ChunkAnalysis.java

@@ -0,0 +1,73 @@
+package com.yonge.nettty.dto;
+
+public class ChunkAnalysis {
+
+	private double startTime;
+
+	private double endTime;
+
+	private double durationTime;
+
+	private double frequency;
+
+	private double splDb;
+
+	private float power;
+
+	public ChunkAnalysis(double startTime, double endTime, double frequency, double splDb, float power) {
+		this.startTime = startTime;
+		this.endTime = endTime;
+		this.frequency = frequency;
+		this.splDb = splDb;
+		this.power = power;
+		this.durationTime = endTime - startTime;
+	}
+
+	public double getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(double startTime) {
+		this.startTime = startTime;
+	}
+
+	public double getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(double endTime) {
+		this.endTime = endTime;
+	}
+
+	public double getDurationTime() {
+		return durationTime;
+	}
+
+	public void setDurationTime(double durationTime) {
+		this.durationTime = durationTime;
+	}
+
+	public double getFrequency() {
+		return frequency;
+	}
+
+	public void setFrequency(double frequency) {
+		this.frequency = frequency;
+	}
+
+	public double getSplDb() {
+		return splDb;
+	}
+
+	public void setSplDb(double splDb) {
+		this.splDb = splDb;
+	}
+
+	public float getPower() {
+		return power;
+	}
+
+	public void setPower(float power) {
+		this.power = power;
+	}
+}

+ 43 - 6
audio-analysis/src/main/java/com/yonge/nettty/entity/NoteAnalysis.java → audio-analysis/src/main/java/com/yonge/nettty/dto/NoteAnalysis.java

@@ -1,12 +1,16 @@
-package com.yonge.nettty.entity;
+package com.yonge.nettty.dto;
 
 public class NoteAnalysis {
 
 	private int index;
 
+	private double startTime;
+
+	private double endTime;
+
 	private double durationTime;
 
-	private double avgPitch;
+	private double frequency;
 
 	private int sectionIndex;
 
@@ -14,11 +18,20 @@ public class NoteAnalysis {
 
 	private int chunks;
 
+	private boolean ignore;
+
 	public NoteAnalysis(int index, int sectionIndex) {
 		this.index = index;
 		this.sectionIndex = sectionIndex;
 	}
 
+	public NoteAnalysis(double startTime, double endTime, double frequency) {
+		this.startTime = startTime;
+		this.endTime = endTime;
+		this.durationTime = endTime - startTime;
+		this.frequency = frequency;
+	}
+
 	public int getIndex() {
 		return index;
 	}
@@ -27,6 +40,22 @@ public class NoteAnalysis {
 		this.index = index;
 	}
 
+	public double getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(double startTime) {
+		this.startTime = startTime;
+	}
+
+	public double getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(double endTime) {
+		this.endTime = endTime;
+	}
+
 	public double getDurationTime() {
 		return durationTime;
 	}
@@ -35,12 +64,12 @@ public class NoteAnalysis {
 		this.durationTime = durationTime;
 	}
 
-	public double getAvgPitch() {
-		return avgPitch;
+	public double getFrequency() {
+		return frequency;
 	}
 
-	public void setAvgPitch(double avgPitch) {
-		this.avgPitch = avgPitch;
+	public void setFrequency(double frequency) {
+		this.frequency = frequency;
 	}
 
 	public int getSectionIndex() {
@@ -67,4 +96,12 @@ public class NoteAnalysis {
 		this.chunks = chunks;
 	}
 
+	public boolean isIgnore() {
+		return ignore;
+	}
+
+	public void setIgnore(boolean ignore) {
+		this.ignore = ignore;
+	}
+
 }

+ 48 - 0
audio-analysis/src/main/java/com/yonge/nettty/dto/SectionAnalysis.java

@@ -0,0 +1,48 @@
+package com.yonge.nettty.dto;
+
+public class SectionAnalysis {
+	
+	//小节下标
+	private int index;
+	
+	//音准
+	private float intonation;
+	
+	//节奏
+	private float tempo;
+	
+	//完整性
+	private float integrity;
+
+	public int getIndex() {
+		return index;
+	}
+
+	public void setIndex(int index) {
+		this.index = index;
+	}
+
+	public float getIntonation() {
+		return intonation;
+	}
+
+	public void setIntonation(float intonation) {
+		this.intonation = intonation;
+	}
+
+	public float getTempo() {
+		return tempo;
+	}
+
+	public void setTempo(float tempo) {
+		this.tempo = tempo;
+	}
+
+	public float getIntegrity() {
+		return integrity;
+	}
+
+	public void setIntegrity(float integrity) {
+		this.integrity = integrity;
+	}
+}

+ 161 - 53
audio-analysis/src/main/java/com/yonge/nettty/dto/UserChannelContext.java

@@ -1,23 +1,26 @@
 package com.yonge.nettty.dto;
 
+import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
+import javax.sound.sampled.AudioFormat;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import be.tarsos.dsp.AudioEvent;
 import be.tarsos.dsp.pitch.PitchDetectionHandler;
 import be.tarsos.dsp.pitch.PitchDetectionResult;
-import be.tarsos.dsp.pitch.PitchDetector;
-import be.tarsos.dsp.pitch.PitchProcessor;
 
-import com.yonge.audio.utils.ArrayUtil;
+import com.yonge.audio.analysis.Signals;
+import com.yonge.audio.analysis.detector.FrequencyDetector;
 import com.yonge.nettty.entity.MusicXmlBasicInfo;
 import com.yonge.nettty.entity.MusicXmlNote;
-import com.yonge.nettty.entity.NoteAnalysis;
+import com.yonge.nettty.entity.MusicXmlSection;
 import com.yonge.netty.server.processor.WaveformWriter;
 
 /**
@@ -34,20 +37,18 @@ public class UserChannelContext implements PitchDetectionHandler {
 
 	private NoteAnalysis processingNote = new NoteAnalysis(0, 0);
 	
+	private AtomicInteger currentSectionIndex = new AtomicInteger(0);
+	
+	private List<NoteAnalysis> doneNoteAnalysisList = new ArrayList<NoteAnalysis>();
+	
 	private byte[] channelBufferBytes = new byte[0];
 	
-	private float[] handlerBufferBytes = new float[0];
-
-	private PitchDetector pitchDetector = PitchProcessor.PitchEstimationAlgorithm.FFT_YIN.getDetector(44100, 1024 * 4);
+	private List<ChunkAnalysis> chunkAnalysisList = new ArrayList<ChunkAnalysis>();
 	
 	public ConcurrentHashMap<Integer, MusicXmlBasicInfo> getSongMusicXmlMap() {
 		return songMusicXmlMap;
 	}
 
-	public void setSongMusicXmlMap(ConcurrentHashMap<Integer, MusicXmlBasicInfo> songMusicXmlMap) {
-		this.songMusicXmlMap = songMusicXmlMap;
-	}
-
 	public WaveformWriter getWaveFileProcessor() {
 		return waveFileProcessor;
 	}
@@ -63,19 +64,35 @@ public class UserChannelContext implements PitchDetectionHandler {
 	public void setProcessingNote(NoteAnalysis processingNote) {
 		this.processingNote = processingNote;
 	}
-
+	
 	public void resetUserInfo() {
 
 		waveFileProcessor = null;
 		processingNote = new NoteAnalysis(0,0);
+		currentSectionIndex = new AtomicInteger(0);
 		channelBufferBytes = new byte[0];
-		handlerBufferBytes = new float[0];
+		doneNoteAnalysisList = new ArrayList<NoteAnalysis>();
+		chunkAnalysisList = new ArrayList<ChunkAnalysis>();
+	}
+	
+	public MusicXmlSection getCurrentMusicSection(Integer songId){
+		MusicXmlBasicInfo musicXmlBasicInfo = null;
+		if (songId == null) {
+			musicXmlBasicInfo = songMusicXmlMap.values().stream().findFirst().get();
+		} else {
+			musicXmlBasicInfo = songMusicXmlMap.get(songId);
+		}
+		return musicXmlBasicInfo.getMusicXmlSectionMap().get(currentSectionIndex.get());
 	}
 
-	public MusicXmlNote getCurrentMusicNote(Integer songId) {
+	public MusicXmlNote getCurrentMusicNote(Integer songId, Integer noteIndex) {
 		if (songMusicXmlMap.size() == 0) {
 			return null;
 		}
+		if(noteIndex == null){
+			noteIndex = processingNote.getIndex();
+		}
+		final int index = noteIndex;
 		MusicXmlBasicInfo musicXmlBasicInfo = null;
 		if (songId == null) {
 			musicXmlBasicInfo = songMusicXmlMap.values().stream().findFirst().get();
@@ -83,14 +100,14 @@ public class UserChannelContext implements PitchDetectionHandler {
 			musicXmlBasicInfo = songMusicXmlMap.get(songId);
 		}
 
-		if (musicXmlBasicInfo != null && processingNote.getIndex() <= getTotalMusicNoteIndexNum(null)) {
-			return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == processingNote.getIndex()).findFirst().get();
+		if (musicXmlBasicInfo != null && index <= getTotalMusicNoteIndex(null)) {
+			return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == index).findFirst().get();
 		}
 
 		return null;
 	}
 
-	public int getTotalMusicNoteIndexNum(Integer songId) {
+	public int getTotalMusicNoteIndex(Integer songId) {
 		if (songMusicXmlMap.size() == 0) {
 			return -1;
 		}
@@ -108,10 +125,14 @@ public class UserChannelContext implements PitchDetectionHandler {
 		return -1;
 	}
 
-	public List<MusicXmlNote> getCurrentMusicSection(Integer songId) {
+	public List<MusicXmlNote> getCurrentMusicSection(Integer songId,Integer sectionIndex) {
 		if (songMusicXmlMap.size() == 0) {
 			return null;
 		}
+		if(sectionIndex == null){
+			sectionIndex = processingNote.getSectionIndex();
+		}
+		final int index = sectionIndex;
 		MusicXmlBasicInfo musicXmlBasicInfo = null;
 		if (songId == null) {
 			musicXmlBasicInfo = songMusicXmlMap.values().stream().findFirst().get();
@@ -120,14 +141,14 @@ public class UserChannelContext implements PitchDetectionHandler {
 		}
 
 		if (musicXmlBasicInfo != null) {
-			return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == processingNote.getSectionIndex())
+			return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == index)
 					.sorted(Comparator.comparing(MusicXmlNote::getMusicalNotesIndex)).collect(Collectors.toList());
 		}
 
 		return null;
 	}
 
-	public int getTotalMusicSectionIndexNum(Integer songId) {
+	public int getTotalMusicSectionIndex(Integer songId) {
 		if (songMusicXmlMap.size() == 0) {
 			return -1;
 		}
@@ -144,6 +165,24 @@ public class UserChannelContext implements PitchDetectionHandler {
 
 		return -1;
 	}
+	
+	public int getMusicSectionIndex(Integer songId, int musicXmlNoteIndex) {
+		if (songMusicXmlMap.size() == 0) {
+			return -1;
+		}
+		MusicXmlBasicInfo musicXmlBasicInfo = null;
+		if (songId == null) {
+			musicXmlBasicInfo = songMusicXmlMap.values().stream().findFirst().get();
+		} else {
+			musicXmlBasicInfo = songMusicXmlMap.get(songId);
+		}
+
+		if (musicXmlBasicInfo != null) {
+			return musicXmlBasicInfo.getMusicXmlInfos().stream().filter(t -> t.getMusicalNotesIndex() == musicXmlNoteIndex).findFirst().get().getMeasureIndex();
+		}
+
+		return -1;
+	}
 
 	public byte[] getChannelBufferBytes() {
 		return channelBufferBytes;
@@ -153,12 +192,105 @@ public class UserChannelContext implements PitchDetectionHandler {
 		this.channelBufferBytes = channelBufferBytes;
 	}
 
-	public float[] getHandlerBufferBytes() {
-		return handlerBufferBytes;
-	}
+	public void handle1(float[] samples, AudioFormat audioFormat){
+		
+		FrequencyDetector frequencyDetector = new FrequencyDetector(samples, audioFormat.getSampleRate(), false);
+		
+		int decibel = Signals.decibels(samples);
+		double pitch = frequencyDetector.getFrequency();
+		
+		LOGGER.info("Frequency:{}  Db:{}", pitch, decibel);
+		
+		double durationTime = 1000 * (samples.length * 2) / audioFormat.getSampleRate() / (audioFormat.getSampleSizeInBits() / 8);
+		
+		// 获取当前音符信息
+		MusicXmlNote musicXmlNote = getCurrentMusicNote(null,null);
 
-	public void setHandlerBufferBytes(float[] handlerBufferBytes) {
-		this.handlerBufferBytes = handlerBufferBytes;
+		if (musicXmlNote == null) {
+			return;
+		}
+		
+		//取出当前处理中的音符信息
+		NoteAnalysis noteAnalysis = getProcessingNote();
+		if(noteAnalysis == null){
+			noteAnalysis = new NoteAnalysis(musicXmlNote.getMusicalNotesIndex(), musicXmlNote.getMeasureIndex());
+		}
+		
+		if(noteAnalysis.getIndex() <= getTotalMusicNoteIndex(null)){
+			
+			double noteDurationTime = noteAnalysis.getDurationTime() + durationTime;
+			noteAnalysis.setDurationTime(noteDurationTime);
+			
+			if(noteDurationTime >= musicXmlNote.getDuration()){
+				
+				if (musicXmlNote.getDontEvaluating()) {
+					noteAnalysis.setIgnore(true);
+				} else {
+					noteAnalysis.setFrequency(noteAnalysis.getTotalPitch() / noteAnalysis.getChunks());
+
+					LOGGER.info("当前音符下标[{}] 预计频率:{} 实际频率:{} 持续时间:{}", noteAnalysis.getIndex(), musicXmlNote.getFrequency(), noteAnalysis.getFrequency(),
+							noteAnalysis.getDurationTime());
+				}
+				doneNoteAnalysisList.add(noteAnalysis);
+				
+				// 准备处理下一个音符
+				noteAnalysis = new NoteAnalysis(musicXmlNote.getMusicalNotesIndex() + 1, getMusicSectionIndex(null, musicXmlNote.getMusicalNotesIndex() + 1));
+				
+				//评分
+				
+			}else{
+				
+				if(pitch < 2000 && pitch > 100){
+					noteAnalysis.setChunks(noteAnalysis.getChunks() + 1);
+					noteAnalysis.setTotalPitch(noteAnalysis.getTotalPitch() + pitch);
+				}
+			}
+			
+			setProcessingNote(noteAnalysis);
+		}
+		
+	}
+	
+	public void handle2(float[] samples, AudioFormat audioFormat){
+		
+		FrequencyDetector frequencyDetector = new FrequencyDetector(samples, audioFormat.getSampleRate(), false);
+		double frequency = frequencyDetector.getFrequency();
+		
+		double splDb = Signals.soundPressureLevel(samples);
+		
+		float power = Signals.power(samples);
+		
+		LOGGER.info("Frequency:{}  SplDb:{}  Power:{}", frequency, splDb, power);
+		
+		double durationTime = 1000 * (samples.length * 2) / audioFormat.getSampleRate() / (audioFormat.getSampleSizeInBits() / 8);
+		
+		double startTime = 0;
+		
+		if(chunkAnalysisList.size() > 0){
+			ChunkAnalysis lastestChunk = chunkAnalysisList.get(chunkAnalysisList.size() - 1);
+			startTime = lastestChunk.getEndTime();
+			
+			if(Math.abs((lastestChunk.getFrequency() - frequency)) > 10 || Math.abs(lastestChunk.getPower() - power) > 0.1){
+				
+				double avgFrequency = chunkAnalysisList.stream().collect(Collectors.averagingDouble(ChunkAnalysis::getFrequency));
+				NoteAnalysis noteAnalysis = new NoteAnalysis(chunkAnalysisList.get(0).getStartTime(), startTime, avgFrequency);
+				doneNoteAnalysisList.add(noteAnalysis);//添加演奏的一个音符
+				
+				//重置
+				chunkAnalysisList.clear();
+				
+				//判断是否需要评分
+				MusicXmlSection musicXmlSection = getCurrentMusicSection(null);
+				if(musicXmlSection != null){
+					if(musicXmlSection.getDuration() < startTime){
+						
+					}
+				}
+				
+			}
+		}
+		
+		chunkAnalysisList.add(new ChunkAnalysis(startTime, startTime + durationTime, frequency, splDb, power));
 	}
 
 	@Override
@@ -171,7 +303,7 @@ public class UserChannelContext implements PitchDetectionHandler {
 		//LOGGER.info("pitch:{} timeStamp:{} endTimeStamp:{} durationTime:{}", pitch, audioEvent.getTimeStamp(), audioEvent.getEndTimeStamp(), durationTime);
 		
 		// 获取当前音符信息
-		MusicXmlNote musicXmlNote = getCurrentMusicNote(null);
+		MusicXmlNote musicXmlNote = getCurrentMusicNote(null,null);
 
 		if (musicXmlNote == null) {
 			return;
@@ -193,11 +325,11 @@ public class UserChannelContext implements PitchDetectionHandler {
 		
 		setProcessingNote(noteAnalysis);
 		
-		if(noteAnalysis.getIndex() <= getTotalMusicNoteIndexNum(null) && noteDurationTime >= musicXmlNote.getDuration()){
+		if(noteAnalysis.getIndex() <= getTotalMusicNoteIndex(null) && noteDurationTime >= musicXmlNote.getDuration()){
 			
-			noteAnalysis.setAvgPitch(noteAnalysis.getTotalPitch()/noteAnalysis.getChunks());
+			noteAnalysis.setFrequency(noteAnalysis.getTotalPitch()/noteAnalysis.getChunks());
 			
-			LOGGER.info("当前音符下标[{}] 预计频率:{} 实际频率:{} 持续时间:{}", noteAnalysis.getIndex() , musicXmlNote.getFrequency(), noteAnalysis.getAvgPitch(), noteAnalysis.getDurationTime());
+			LOGGER.info("当前音符下标[{}] 预计频率:{} 实际频率:{} 持续时间:{}", noteAnalysis.getIndex() , musicXmlNote.getFrequency(), noteAnalysis.getFrequency(), noteAnalysis.getDurationTime());
 			
 			// 准备处理下一个音符
 			setProcessingNote(noteAnalysis = new NoteAnalysis(musicXmlNote.getMusicalNotesIndex() + 1,musicXmlNote.getMeasureIndex()));
@@ -232,28 +364,4 @@ public class UserChannelContext implements PitchDetectionHandler {
 		}*/
 	}
 
-	private float getPitch(float[] audioBuffer, int bufferSize) {
-
-		int blankNum = audioBuffer.length % bufferSize;
-		float[] zeroBytes = new float[blankNum];
-
-		audioBuffer = ArrayUtil.mergeFloat(audioBuffer, zeroBytes);
-
-		int times = audioBuffer.length / bufferSize;
-
-		float totalPitch = 0f;
-
-		float[] bufferByte = new float[bufferSize];
-		for (int i = 0; i < times; i++) {
-			bufferByte = ArrayUtil.extractFloat(audioBuffer, i * bufferSize, (i + 1) * bufferSize);
-			float pitch = pitchDetector.getPitch(bufferByte).getPitch();
-			if(pitch == -1){
-				continue;
-			}
-			totalPitch += pitch;
-		}
-
-		return totalPitch / times;
-	}
-
 }

+ 34 - 0
audio-analysis/src/main/java/com/yonge/nettty/entity/MusicXmlBasicInfo.java

@@ -1,7 +1,11 @@
 package com.yonge.nettty.entity;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
 
 public class MusicXmlBasicInfo {
 
@@ -27,6 +31,8 @@ public class MusicXmlBasicInfo {
 
 	private List<MusicXmlNote> musicXmlInfos = new ArrayList<MusicXmlNote>();
 
+	private Map<Integer, MusicXmlSection> musicXmlSectionMap = new HashMap<Integer, MusicXmlSection>();
+
 	public Integer getId() {
 		return id;
 	}
@@ -114,4 +120,32 @@ public class MusicXmlBasicInfo {
 	public void setMusicXmlInfos(List<MusicXmlNote> musicXmlInfos) {
 		this.musicXmlInfos = musicXmlInfos;
 	}
+
+	public Map<Integer, MusicXmlSection> getMusicXmlSectionMap() {
+
+		if (musicXmlSectionMap.size() == 0) {
+			Map<Integer, List<MusicXmlNote>> map = musicXmlInfos.stream().collect(Collectors.groupingBy(MusicXmlNote::getMeasureIndex));
+
+			List<MusicXmlNote> noteList = null;
+			MusicXmlSection section = null;
+			for (Entry<Integer, List<MusicXmlNote>> entry : map.entrySet()) {
+				noteList = entry.getValue();
+
+				section = new MusicXmlSection();
+
+				section.setStartTime(noteList.stream().map(t -> t.getTimeStamp()).distinct().min(Double::compareTo).get());
+				section.setDuration(noteList.stream().mapToDouble(t -> t.getDuration()).sum());
+				section.setNoteNum(noteList.size());
+				section.setIndex(entry.getKey());
+
+				musicXmlSectionMap.put(entry.getKey(), section);
+			}
+		}
+
+		return musicXmlSectionMap;
+	}
+
+	public void setMusicXmlSectionMap(Map<Integer, MusicXmlSection> musicXmlSectionMap) {
+		this.musicXmlSectionMap = musicXmlSectionMap;
+	}
 }

+ 7 - 7
audio-analysis/src/main/java/com/yonge/nettty/entity/MusicXmlNote.java

@@ -6,10 +6,10 @@ package com.yonge.nettty.entity;
 public class MusicXmlNote {
 
 	// 音符起始时间戳(第一个音符是0ms)
-	private int timeStamp;
+	private double timeStamp;
 
 	// 当前音符持续的播放时间(ms)
-	private int duration;
+	private double duration;
 
 	// 当前音符的频率
 	private float frequency;
@@ -26,19 +26,19 @@ public class MusicXmlNote {
 	// 当前音符在整个曲谱中的下标(从0开始)
 	private int musicalNotesIndex;
 
-	public int getTimeStamp() {
+	public double getTimeStamp() {
 		return timeStamp;
 	}
 
-	public void setTimeStamp(int timeStamp) {
+	public void setTimeStamp(double timeStamp) {
 		this.timeStamp = timeStamp;
 	}
 
-	public int getDuration() {
+	public double getDuration() {
 		return duration;
 	}
 
-	public void setDuration(int duration) {
+	public void setDuration(double duration) {
 		this.duration = duration;
 	}
 
@@ -66,7 +66,7 @@ public class MusicXmlNote {
 		this.measureIndex = measureIndex;
 	}
 
-	public boolean isDontEvaluating() {
+	public boolean getDontEvaluating() {
 		return dontEvaluating;
 	}
 

+ 50 - 0
audio-analysis/src/main/java/com/yonge/nettty/entity/MusicXmlSection.java

@@ -0,0 +1,50 @@
+package com.yonge.nettty.entity;
+
+/**
+ * 小节信息
+ */
+public class MusicXmlSection {
+
+	private double startTime;
+
+	// 当前小节持续的播放时间(ms)
+	private double duration;
+
+	// 音符数量
+	private int noteNum;
+
+	private int index;
+
+	public double getDuration() {
+		return duration;
+	}
+
+	public void setDuration(double duration) {
+		this.duration = duration;
+	}
+
+	public int getNoteNum() {
+		return noteNum;
+	}
+
+	public void setNoteNum(int noteNum) {
+		this.noteNum = noteNum;
+	}
+
+	public double getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(double startTime) {
+		this.startTime = startTime;
+	}
+
+	public int getIndex() {
+		return index;
+	}
+
+	public void setIndex(int index) {
+		this.index = index;
+	}
+
+}

+ 0 - 61
audio-analysis/src/main/java/com/yonge/netty/server/NioAudioInputStream.java

@@ -1,61 +0,0 @@
-package com.yonge.netty.server;
-
-import be.tarsos.dsp.io.TarsosDSPAudioFormat;
-import be.tarsos.dsp.io.TarsosDSPAudioInputStream;
-import org.springframework.stereotype.Component;
-
-import javax.sound.sampled.AudioFormat;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-
-/**
- * @Author Joburgess
- * @Date 2021/8/4 0004
- */
-public class NioAudioInputStream implements TarsosDSPAudioInputStream {
-
-    private RandomAccessFile randomAccessFile;
-    private AudioFormat format;
-
-    private long position = 44;
-
-    public NioAudioInputStream() {
-    }
-
-    public NioAudioInputStream(RandomAccessFile randomAccessFile, AudioFormat audioFormat) {
-        this.randomAccessFile = randomAccessFile;
-        this.format = audioFormat;
-    }
-
-    @Override
-    public long skip(long bytesToSkip) throws IOException {
-        return randomAccessFile.skipBytes((int) bytesToSkip);
-    }
-
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        randomAccessFile.seek(position);
-        int read = randomAccessFile.read(b, off, len);
-        if(read>0){
-            position += read;
-        }
-        return read;
-    }
-
-    @Override
-    public void close() throws IOException {
-        randomAccessFile.close();
-    }
-
-    @Override
-    public TarsosDSPAudioFormat getFormat() {
-        boolean isSigned = format.getEncoding() == AudioFormat.Encoding.PCM_SIGNED;
-        TarsosDSPAudioFormat tarsosDSPFormat = new TarsosDSPAudioFormat(format.getSampleRate(), format.getSampleSizeInBits(), format.getChannels(), isSigned, format.isBigEndian());
-        return tarsosDSPFormat;
-    }
-
-    @Override
-    public long getFrameLength() {
-        return 0;
-    }
-}

+ 17 - 7
audio-analysis/src/main/java/com/yonge/netty/server/messagehandler/BinaryWebSocketFrameHandler.java

@@ -8,7 +8,10 @@ import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
 
+import java.applet.AudioClip;
 import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 
 import javax.sound.sampled.AudioFormat;
 
@@ -19,10 +22,10 @@ import be.tarsos.dsp.AudioDispatcher;
 import be.tarsos.dsp.io.TarsosDSPAudioFloatConverter;
 import be.tarsos.dsp.io.TarsosDSPAudioFormat;
 import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;
-import be.tarsos.dsp.pitch.PitchDetector;
 import be.tarsos.dsp.pitch.PitchProcessor;
 import be.tarsos.dsp.pitch.PitchProcessor.PitchEstimationAlgorithm;
 
+import com.yonge.audio.analysis.AudioFloatConverter;
 import com.yonge.audio.utils.ArrayUtil;
 import com.yonge.nettty.dto.UserChannelContext;
 import com.yonge.netty.server.NettyChannelManager;
@@ -57,7 +60,7 @@ public class BinaryWebSocketFrameHandler extends SimpleChannelInboundHandler<Bin
 	/**
 	 * @describe 采样大小
 	 */
-	private int bufferSize = 1024 * 4;
+	private int bufferSize = 1024 * 8;
 	/**
 	 * @describe 帧覆盖大小
 	 */
@@ -69,8 +72,7 @@ public class BinaryWebSocketFrameHandler extends SimpleChannelInboundHandler<Bin
 
 	private AudioFormat audioFormat = new AudioFormat(sampleRate, bitsPerSample, channels, signed, bigEndian);
 
-	private TarsosDSPAudioFloatConverter converter = TarsosDSPAudioFloatConverter.getConverter(new TarsosDSPAudioFormat(sampleRate, bitsPerSample, channels,
-			signed, bigEndian));
+	private AudioFloatConverter converter = AudioFloatConverter.getConverter(audioFormat);
 
 	private PitchEstimationAlgorithm algorithm = PitchProcessor.PitchEstimationAlgorithm.FFT_YIN;
 
@@ -97,6 +99,8 @@ public class BinaryWebSocketFrameHandler extends SimpleChannelInboundHandler<Bin
 
 	private String tmpFileDir = "e:/soundRecords/";
 	
+	private SimpleDateFormat sdf =new SimpleDateFormat("yyMMddHHmmSS");
+	
 	@Override
 	protected void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame frame) throws Exception {
 
@@ -118,7 +122,7 @@ public class BinaryWebSocketFrameHandler extends SimpleChannelInboundHandler<Bin
 			// 写录音文件
 			WaveformWriter waveFileProcessor = channelContext.getWaveFileProcessor();
 			if (waveFileProcessor == null) {
-				File file = new File(tmpFileDir + user + "_" + System.currentTimeMillis() + ".wav");
+				File file = new File(tmpFileDir + user + "_" + sdf.format(new Date()) + ".wav");
 				waveFileProcessor = new WaveformWriter(file.getAbsolutePath());
 				channelContext.setWaveFileProcessor(waveFileProcessor);
 			}
@@ -132,11 +136,17 @@ public class BinaryWebSocketFrameHandler extends SimpleChannelInboundHandler<Bin
 				byte[] bufferData = ArrayUtil.extractByte(channelContext.getChannelBufferBytes(), 0, bufferSize);
 				channelContext.setChannelBufferBytes(ArrayUtil.extractByte(channelContext.getChannelBufferBytes(), bufferSize - 1, totalLength - 1));
 				
+				float[] sampleFloats = new float[bufferSize/2];
+				
+				converter.toFloatArray(bufferData,sampleFloats);
+				
+				channelContext.handle1(sampleFloats, audioFormat);
+				
 				totalLength = channelContext.getChannelBufferBytes().length;
 
-				AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(bufferData, audioFormat, bufferSize, overlap);
+				/*AudioDispatcher dispatcher = AudioDispatcherFactory.fromByteArray(bufferData, audioFormat, bufferSize, overlap);
 				dispatcher.addAudioProcessor(new PitchProcessor(algorithm, sampleRate, bufferSize, channelContext));
-				dispatcher.run();
+				dispatcher.run();*/
 			}
 
 		} finally {