diff --git a/core-java-modules/core-java-os/pom.xml b/core-java-modules/core-java-os/pom.xml index 5f00b709e3..04db6a53f3 100644 --- a/core-java-modules/core-java-os/pom.xml +++ b/core-java-modules/core-java-os/pom.xml @@ -16,6 +16,12 @@ + + org.junit.jupiter + junit-jupiter-engine + 5.7.2 + test + org.apache.commons commons-collections4 diff --git a/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/App.java b/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/App.java new file mode 100644 index 0000000000..afd7687528 --- /dev/null +++ b/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/App.java @@ -0,0 +1,34 @@ +package com.baeldung.example.soundapi; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; + +public class App { + public static void main(String[] args) throws Exception { + + AudioFormat format = buildAudioFormatInstance(); + + SoundRecorder soundRecorder = new SoundRecorder(); + soundRecorder.build(format); + + System.out.println("Start recording ...."); + soundRecorder.start(); + Thread.sleep(20000); + soundRecorder.stop(); + + WaveDataUtil wd = new WaveDataUtil(); + Thread.sleep(3000); + wd.saveToFile("/SoundClip", AudioFileFormat.Type.WAVE, soundRecorder.getAudioInputStream()); + } + + public static AudioFormat buildAudioFormatInstance() { + ApplicationProperties aConstants = new ApplicationProperties(); + AudioFormat.Encoding encoding = aConstants.ENCODING; + float rate = aConstants.RATE; + int channels = aConstants.CHANNELS; + int sampleSize = aConstants.SAMPLE_SIZE; + boolean bigEndian = aConstants.BIG_ENDIAN; + + return new AudioFormat(encoding, rate, sampleSize, channels, (sampleSize / 8) * channels, rate, bigEndian); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/ApplicationProperties.java b/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/ApplicationProperties.java new file mode 100644 index 0000000000..985229b969 --- /dev/null +++ b/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/ApplicationProperties.java @@ -0,0 +1,10 @@ +package com.baeldung.example.soundapi; +import javax.sound.sampled.AudioFormat; + +public class ApplicationProperties { + public final AudioFormat.Encoding ENCODING = AudioFormat.Encoding.PCM_SIGNED; + public final float RATE = 44100.0f; + public final int CHANNELS = 1; + public final int SAMPLE_SIZE = 16; + public final boolean BIG_ENDIAN = true; +} \ No newline at end of file diff --git a/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/SoundRecorder.java b/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/SoundRecorder.java new file mode 100644 index 0000000000..cae91ee74f --- /dev/null +++ b/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/SoundRecorder.java @@ -0,0 +1,123 @@ +package com.baeldung.example.soundapi; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.TargetDataLine; + +public class SoundRecorder implements Runnable { + private AudioInputStream audioInputStream; + private AudioFormat format; + public Thread thread; + private double duration; + + public SoundRecorder() { + super(); + } + + public SoundRecorder(AudioFormat format) { + this.format = format; + } + + public SoundRecorder build(AudioFormat format) { + this.format = format; + return this; + } + + public void start() { + thread = new Thread(this); + thread.setName("Capture Microphone"); + thread.start(); + } + + public void stop() { + thread = null; + } + + @Override + public void run() { + duration = 0; + + try (final ByteArrayOutputStream out = new ByteArrayOutputStream(); final TargetDataLine line = getTargetDataLineForRecord();) { + + int frameSizeInBytes = format.getFrameSize(); + int bufferLengthInFrames = line.getBufferSize() / 8; + final int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes; + buildByteOutputStream(out, line, frameSizeInBytes, bufferLengthInBytes); + this.audioInputStream = new AudioInputStream(line); + setAudioInputStream(convertToAudioIStream(out, frameSizeInBytes)); + audioInputStream.reset(); + } catch (IOException ex) { + ex.printStackTrace(); + } catch (Exception ex) { + ex.printStackTrace(); + return; + } + } + + public void buildByteOutputStream(final ByteArrayOutputStream out, final TargetDataLine line, int frameSizeInBytes, final int bufferLengthInBytes) throws IOException { + final byte[] data = new byte[bufferLengthInBytes]; + int numBytesRead; + + line.start(); + while (thread != null) { + if ((numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1) { + break; + } + out.write(data, 0, numBytesRead); + } + } + + private void setAudioInputStream(AudioInputStream aStream) { + this.audioInputStream = aStream; + } + + public AudioInputStream convertToAudioIStream(final ByteArrayOutputStream out, int frameSizeInBytes) { + byte audioBytes[] = out.toByteArray(); + ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes); + AudioInputStream audioStream = new AudioInputStream(bais, format, audioBytes.length / frameSizeInBytes); + long milliseconds = (long) ((audioInputStream.getFrameLength() * 1000) / format.getFrameRate()); + duration = milliseconds / 1000.0; + System.out.println("Recorded duration in seconds:" + duration); + return audioStream; + } + + public TargetDataLine getTargetDataLineForRecord() { + TargetDataLine line; + DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); + if (!AudioSystem.isLineSupported(info)) { + return null; + } + try { + line = (TargetDataLine) AudioSystem.getLine(info); + line.open(format, line.getBufferSize()); + } catch (final Exception ex) { + return null; + } + return line; + } + + public AudioInputStream getAudioInputStream() { + return audioInputStream; + } + + public AudioFormat getFormat() { + return format; + } + + public void setFormat(AudioFormat format) { + this.format = format; + } + + public Thread getThread() { + return thread; + } + + public double getDuration() { + return duration; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/WaveDataUtil.java b/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/WaveDataUtil.java new file mode 100644 index 0000000000..7d63e66fdd --- /dev/null +++ b/core-java-modules/core-java-os/src/main/java/com/baeldung/example/soundapi/WaveDataUtil.java @@ -0,0 +1,34 @@ +package com.baeldung.example.soundapi; + +import java.io.File; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; + +public class WaveDataUtil { + public boolean saveToFile(String name, AudioFileFormat.Type fileType, AudioInputStream audioInputStream) { + System.out.println("Saving..."); + if (null == name || null == fileType || audioInputStream == null) { + return false; + } + File myFile = new File(name + "." + fileType.getExtension()); + try { + audioInputStream.reset(); + } catch (Exception e) { + return false; + } + int i = 0; + while (myFile.exists()) { + String temp = "" + i + myFile.getName(); + myFile = new File(temp); + } + try { + AudioSystem.write(audioInputStream, fileType, myFile); + } catch (Exception ex) { + return false; + } + System.out.println("Saved " + myFile.getAbsolutePath()); + return true; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-os/src/test/java/com/baeldung/example/soundapi/AppTest.java b/core-java-modules/core-java-os/src/test/java/com/baeldung/example/soundapi/AppTest.java new file mode 100644 index 0000000000..ff00523eec --- /dev/null +++ b/core-java-modules/core-java-os/src/test/java/com/baeldung/example/soundapi/AppTest.java @@ -0,0 +1,75 @@ +package com.baeldung.example.soundapi; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import java.io.ByteArrayOutputStream; + +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.TargetDataLine; + +public class AppTest { + + AudioFormat af = App.buildAudioFormatInstance(); + SoundRecorder soundRecorder = new SoundRecorder(); + + @Test + public void when_run_save_SoundClip() { + + soundRecorder.build(af); + try { + soundRecorder.start(); + Thread.sleep(20000); + soundRecorder.stop(); + } catch (InterruptedException ex) { + fail("Exception: " + ex); + } + + } + + @Test + public void when_run_get_targetdataline() { + soundRecorder.setFormat(af); + Assertions.assertDoesNotThrow(() -> soundRecorder.getTargetDataLineForRecord()); + } + + @Test + public void when_run_build_byte_ouptstream() { + + soundRecorder.setFormat(af); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TargetDataLine tLine = soundRecorder.getTargetDataLineForRecord(); + + int frameSizeInBytes = af.getFrameSize(); + int bufferLengthInFrames = tLine.getBufferSize() / 8; + final int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes; + + Assertions.assertDoesNotThrow( + () -> soundRecorder.buildByteOutputStream(out, tLine, frameSizeInBytes, bufferLengthInBytes)); + } + + @Test + public void when_run_then_save_file() { + soundRecorder.setFormat(af); + soundRecorder.build(af); + try { + soundRecorder.start(); + Thread.sleep(20000); + soundRecorder.stop(); + WaveDataUtil wd = new WaveDataUtil(); + Thread.sleep(3000); + boolean saveFile = wd.saveToFile("/SoundClip", AudioFileFormat.Type.WAVE, + soundRecorder.getAudioInputStream()); + + assertEquals(saveFile, true); + + } catch (InterruptedException ex) { + fail("Exception: " + ex); + } + + } + +}