Skip to content

How to get the level of the TargetDataLine?

Aaron Gokaslan edited this page Apr 12, 2014 · 1 revision

Another method of getting the instantaneous volume is the target data line. This method simply polls the targetDataLine of the Microphone for the level of the audio. There is an implementation of this method on the interface of TargetDataLine. Unfortunately, the method appears to be unimplemented on most systems. As such, here are some custom methods that will allow you to poll that data.

Here are the classes:

public class Main {

	public static void main(String[] args){
		MicrophoneAnalyzer mic = new MicrophoneAnalyzer(FLACFileWriter.FLAC);
		System.out.println("HELLO");
		mic.open();
		while(true){
			byte[] buffer = new byte[mic.getTargetDataLine().getFormat().getFrameSize()];
			mic.getTargetDataLine().read(buffer, 0, buffer.length);
			try{
				System.out.println(getLevel(mic.getAudioFormat(), buffer));
			}
			catch(Exception e){
				System.out.println("ERROR");
				e.printStackTrace();
			}
		}
	}

	public static double getLevel(AudioFormat af, byte[] chunk) throws IOException{
		PCMSigned8Bit converter = new PCMSigned8Bit(af);
		if(chunk.length != converter.getRequiredChunkByteSize())
			return -1;

		AudioInputStream ais = converter.convert(chunk);
		ais.read(chunk, 0, chunk.length);

		long lSum = 0;
		for(int i=0; i<chunk.length; i++)
			lSum = lSum + chunk[i];

		double dAvg = lSum / chunk.length;
		double sumMeanSquare = 0d;

		for(int j=0; j<chunk.length; j++)

			sumMeanSquare = sumMeanSquare + Math.pow(chunk[j] - dAvg, 2d);
    
		double averageMeanSquare = sumMeanSquare / chunk.length;

		return (Math.pow(averageMeanSquare,0.5d));
	}
}

The method I used only works on 8bitPCM so we have to convert the encoding to that using these two classes. Here is the general abstract converter class.

import java.io.ByteArrayInputStream;
import java.io.IOException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

abstract class AbstractSignedLevelConverter
{
    private AudioFormat srcf;

    public AbstractSignedLevelConverter(AudioFormat sourceFormat)
    {
        srcf = sourceFormat;
    }


    protected AudioInputStream convert(byte[] chunk)
    {
        AudioInputStream ais = null;
        if(AudioSystem.isConversionSupported(   AudioFormat.Encoding.PCM_SIGNED,
                                                            srcf))
        {
            if(srcf.getEncoding() != AudioFormat.Encoding.PCM_SIGNED)
                ais = AudioSystem.getAudioInputStream(
                        AudioFormat.Encoding.PCM_SIGNED,
                        new AudioInputStream(new ByteArrayInputStream(chunk),
                                                    srcf,
                                                    chunk.length * srcf.getFrameSize()));
            else
                ais = new AudioInputStream(new ByteArrayInputStream(chunk),
                                                    srcf,
                                                    chunk.length * srcf.getFrameSize());
        }

        return ais;
    }

    abstract public double convertToLevel(byte[] chunk)  throws IOException;
    
    public int getRequiredChunkByteSize()
    {
        return srcf.getFrameSize();
    }
}

And here is the one for 8BitPCM

import java.io.IOException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;

public class PCMSigned8Bit extends AbstractSignedLevelConverter
{
    PCMSigned8Bit(AudioFormat sourceFormat)
    {
        super(sourceFormat);
    }

    public double convertToLevel(byte[] chunk) throws IOException
    {
        if(chunk.length != getRequiredChunkByteSize())
            return -1;

        AudioInputStream ais = convert(chunk);
        ais.read(chunk, 0, chunk.length);

        return (double)chunk[0];
    }
    
    
    
    
}