In order to make recording sound in Java easier for developers, we created a generic and reusable utility class which is described below. Code is based on the Java Sound API.

The following class diagram depicts how the utility class is designed:

 SoundRecordingUtil class diagram

As we can see, this class has three public methods start(), stop() and save() which should be invoked sequentially. The start()method will run an infinite loop to store captured audio data into a byte array, until the method stop() is called; and the save()method stores recorded audio to the specified file in .wav format.

Here’s full source code of the utility class:

package net.codejava.sound;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;

/**
 * A utility class provides general functions for recording sound.
 * @author www.codejava.net
 *
 */
public class SoundRecordingUtil {
	private static final int BUFFER_SIZE = 4096;
	private ByteArrayOutputStream recordBytes;
	private TargetDataLine audioLine;
	private AudioFormat format;

	private boolean isRunning;

	/**
	 * Defines a default audio format used to record
	 */
	AudioFormat getAudioFormat() {
		float sampleRate = 16000;
		int sampleSizeInBits = 8;
		int channels = 2;
		boolean signed = true;
		boolean bigEndian = true;
		return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,
				bigEndian);
	}

	/**
	 * Start recording sound.
	 * @throws LineUnavailableException if the system does not support the specified 
	 * audio format nor open the audio data line.
	 */
	public void start() throws LineUnavailableException {
		format = getAudioFormat();
		DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);

		// checks if system supports the data line
		if (!AudioSystem.isLineSupported(info)) {
			throw new LineUnavailableException(
					"The system does not support the specified format.");
		}

		audioLine = AudioSystem.getTargetDataLine(format);

		audioLine.open(format);
		audioLine.start();

		byte[] buffer = new byte[BUFFER_SIZE];
		int bytesRead = 0;

		recordBytes = new ByteArrayOutputStream();
		isRunning = true;

		while (isRunning) {
			bytesRead = audioLine.read(buffer, 0, buffer.length);
			recordBytes.write(buffer, 0, bytesRead);
		}
	}

	/**
	 * Stop recording sound.
	 * @throws IOException if any I/O error occurs.
	 */
	public void stop() throws IOException {
		isRunning = false;
		
		if (audioLine != null) {
			audioLine.drain();
			audioLine.close();
		}
	}

	/**
	 * Save recorded sound data into a .wav file format.
	 * @param wavFile The file to be saved.
	 * @throws IOException if any I/O error occurs.
	 */
	public void save(File wavFile) throws IOException {
		byte[] audioData = recordBytes.toByteArray();
		ByteArrayInputStream bais = new ByteArrayInputStream(audioData);
		AudioInputStream audioInputStream = new AudioInputStream(bais, format,
				audioData.length / format.getFrameSize());

		AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, wavFile);

		audioInputStream.close();
		recordBytes.close();
	}
}

To use this utility class, follow these steps:

    • Create a new instance of the SoundRecordingUtilclass.
    • Run the start() method in a separate thread because it will block the currently executing thread.
    • In another thread (either main thread or a new one):
      • Call the stop() method after a specified duration.
      • Call the save() method to store the recorded sound to a WAV file.

 

Here’s an example program that tests the above utility class:

package net.codejava.sound;

import java.io.File;
import java.io.IOException;

import javax.sound.sampled.LineUnavailableException;

/**
 * A sample program that tests the SoundRecordingUtil utility class.
 * @author www.codejava.net
 *
 */
public class TestSoundRecordingUtil {
	private static final int RECORD_TIME = 60000;	// 60 seconds	
	
	public static void main(String[] args) {
		File wavFile = new File("E:/Test/Record.wav");
		
		final SoundRecordingUtil recorder = new SoundRecordingUtil();
		
		// create a separate thread for recording
		Thread recordThread = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("Start recording...");
					recorder.start();
				} catch (LineUnavailableException ex) {
					ex.printStackTrace();
					System.exit(-1);
				}				
			}
		});
		
		recordThread.start();
		
		try {
			Thread.sleep(RECORD_TIME);
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		
		try {
			recorder.stop();
			recorder.save(wavFile);
			System.out.println("STOPPED");
		} catch (IOException ex) {
			ex.printStackTrace();
		}
		
		System.out.println("DONE");
	}

}

This program will record sound for 60 seconds and save the audio to a file under E:/Test/Record.wav. You may change the duration and file location to suite your need.

 

Related Java Sound Tutorials:

 

Other Java Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Add comment

   


Comments 

#4Minghsien,Chan2020-03-21 06:20
Find the core reason:
same as #1 Rich's comment below.

Thank you all.
Quote
#3Minghsien,Chan2020-03-21 01:25
The example program that tests the above utility class ... it fails to work.
Recording function did not work as you expected.
Quote
#2Nam2014-11-30 23:01
Hi Rich,

Thank you for sharing your information which others can find useful.

Have a nice day!
Quote
#1Rich2014-11-30 18:30
Thank you for making this example and class. Quick and easily understood.
On my WIN7 machine audioLine.drain(); seems to cause a hang, so I substituted audioLine.flush(); and accept that the remaining data is discarded. My audio format is 16bit, 16,000 sampling, 1 channel.
Quote