All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.musicg.wave.Wave Maven / Gradle / Ivy

Go to download

Musicg is a lightweight audio analysis library, written in Java, with the purpose of extracting both high level and low level audio features.

The newest version!
/*
 * 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, Cloneable{

	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();
	}

	/**
	 * Length of the wave in second
	 *
	 * @return length in second
	 */
	public float getLengthInSeconds() {

		float second = (float) waveHeader.getSubChunk2Size() / waveHeader.getByteRate();
		return second;
	}

	public Wave clone() {
		Wave result = new Wave();
		result.waveHeader = waveHeader.clone();
		if (data != null)
			result.data = data.clone();
		if (fingerprint != null)
			result.fingerprint = fingerprint.clone();
		return result;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy