Java Sound API:捕捉麦克风

2023/07/03

1. 概述

在本文中,我们将介绍如何使用Java捕获麦克风并录制传入的音频以将其保存为WAV文件。为了捕获来自麦克风的传入声音,我们使用Java Sound API,这是Java生态系统的一部分。

Java Sound API是一个强大的API,用于捕获、处理和播放音频,由4个包组成。我们将重点介绍javax.sound.sampled包,它提供了捕获传入音频所需的所有接口和类

2. 什么是TargetDataLine?

TargetDataLine是一种DataLine对象,我们使用它来捕获和读取与音频相关的数据,它从麦克风等音频捕获设备捕获数据。该接口提供读取和捕获数据所需的所有方法,并从目标数据线的缓冲区中读取数据。

我们可以调用AudioSystem的getLine()方法并为其提供DataLine.Info对象,该对象提供音频的所有传输控制方法。Oracle文档详细解释了Java Sound API的工作原理。

让我们来看看用Java从麦克风捕获音频所需的步骤。

3. 捕捉声音的步骤

为了保存捕获的音频,Java支持AU、AIFF、AIFC、SND和WAVE文件格式。我们将使用WAVE(.wav)文件格式来保存我们的文件。

该过程的第一步是初始化AudioFormat实例,AudioFormat通知Java如何解释和处理传入声音流中的信息位,我们在示例中使用以下AudioFormat类构造函数:

AudioFormat(AudioFormat.Encoding encoding, float sampleRate, int sampleSizeInBits, int channels, int frameSize, float frameRate, boolean bigEndian)

之后,我们打开一个DataLine.Info对象,该对象保存与数据线(输入)相关的所有信息。使用DataLine.Info对象,我们可以创建TargetDataLine的实例,它将所有传入数据读取到音频流中。为了生成TargetDataLine实例,我们使用AudioSystem.getLine()方法并传递DataLine.Info对象:

line = (TargetDataLine) AudioSystem.getLine(info);

line是TargetDataLine实例,info是DataLine.Info实例。

创建完成后,我们可以打开line来读取所有传入的声音,我们可以使用AudioInputStream来读取传入的数据。总之,我们可以将这些数据写入WAV文件并关闭所有流

为了理解这个过程,让我们来看一个记录输入声音的小程序。

4. 示例应用

要了解Java Sound API的实际应用,让我们创建一个简单的程序。我们将它分成三个部分,首先构建AudioFormat,其次构建TargetDataLine,最后将数据保存为文件。

4.1 构建AudioFormat

AudioFormat类定义了TargetDataLine实例可以捕获的数据类型,因此,第一步是在我们打开新数据线之前初始化AudioFormat类实例。App类是应用程序的主类并进行所有调用,我们在一个名为ApplicationProperties的常量类中定义AudioFormat的属性。我们绕过所有必要的参数构建AudioFormat实例:

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

现在我们已经准备好AudioFormat,我们可以继续构建TargetDataLine实例。

4.2 构建TargetDataLine

我们使用TargetDataLine类从麦克风读取音频数据,在我们的示例中,我们获取并运行SoundRecorder类中的TargetDataLine。getTargetDataLineForRecord()方法构建TargetDataLine实例。

我们读取并处理音频输入并将其转储到AudioInputStream对象中,我们创建TargetDataLine实例的方式是:

private TargetDataLine getTargetDataLineForRecord() {
    TargetDataLine line;
    DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
    if (!AudioSystem.isLineSupported(info)) {
        return null;
    }
    line = (TargetDataLine) AudioSystem.getLine(info);
    line.open(format, line.getBufferSize());
    return line;
}

4.3 构建和填充AudioInputStream

到目前为止,在我们的示例中,我们已经创建了一个AudioFormat实例并将其应用于TargetDataLine,并打开数据线以读取音频数据。我们还创建了一个线程来帮助自动运行SoundRecorder实例,我们首先在线程运行时构建一个字节输出流,然后将其转换为一个AudioInputStream实例。我们构建AudioInputStream实例所需的参数是:

int frameSizeInBytes = format.getFrameSize();
int bufferLengthInFrames = line.getBufferSize() / 8;
final int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;

请注意,在上面的代码中,我们将bufferSize除以8。我们这样做是为了使缓冲区和数组的长度相同,以便记录器可以在读取数据后立即将数据传送到line。

现在我们已经初始化了所有需要的参数,下一步是构建字节输出流。下一步是将生成的输出流(捕获的声音数据)转换为AudioInputStream实例。

buildByteOutputStream(out, line, frameSizeInBytes, bufferLengthInBytes);
this.audioInputStream = new AudioInputStream(line);

setAudioInputStream(convertToAudioIStream(out, frameSizeInBytes));
audioInputStream.reset();

在设置InputStream之前,我们将构建字节OutputStream

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

然后我们将字节OutStream转换为AudioInputStream

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;
    return audioStream;
}

4.4 将AudioInputStream保存到Wav文件

我们已经创建并填充了AudioInputStream,并将其存储为SoundRecorder类的成员变量。我们将使用SoundRecorder实例getter属性在App类中检索此AudioInputStream并将其传递给WaveDataUtil类:

wd.saveToFile("/SoundClip", AudioFileFormat.Type.WAVE, soundRecorder.getAudioInputStream());

WaveDataUtil类具有将AudioInputStream转换为.wav文件的代码:

AudioSystem.write(audioInputStream, fileType, myFile);

5. 总结

本文展示了一个使用Java Sound API通过麦克风捕获和录制音频的快速示例。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章