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;
			if (signal[i] > 32767) {
				signal[i] = 32767;
			} else if (signal[i] < -32768) {
				signal[i] = -32768;
			}
		}
	}

	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 + "  energy:" + energy(sampleFloats) + "	power:" + power(sampleFloats) + "  rms:" + rms(sampleFloats) + "	splDb: "
					+ splDb + "	frequency: " + f);
		}
	}
}
