Java实现音频流录制或保存wav格式到本地

在Java中处理音频数据并保存到本地文件是一项常见的需求。你可以使用Java标准库中的javax.sound.sampled包来完成这项任务。下面是一个详细的指南,介绍如何录制音频并将其保存为WAV文件。

1. 准备工作

首先,确保你的开发环境中已经包含了Java标准库。javax.sound.sampled是Java自带的,因此你不需要额外安装其他库。

2. WAV音频文件格式

WAV(Waveform Audio File Format)音频文件格式,是一种无压缩的音频文件格式,它使用PCM(Pulse Code Modulation)编码,将模拟音频信号转换为数字音频信号。它是Windows操作系统中默认的音频文件格式,通常用于存储高质量的音频数据,比如音乐、录音等。

WAV文件的格式:


WAV文件包含了音频数据的采样率比特率声道数等信息,并且可以包含额外的标签信息。

WAV文件的特点是无损压缩,即不会失去任何音频质量。它可以支持多种音频编码方式,比如PCM编码和ADPCM编码等。由于WAV文件不进行任何压缩,所以它的文件大小相对较大,这也是WAV文件的一个缺点。除了音频数据外,WAV文件还可以包含元数据,比如歌曲的歌手、专辑名称、发行日期等信息。这些元数据可以帮助用户更好地管理和查找自己的音乐文件。

pcm和wav的区别:

  • PCM和WAV都是数字音频的编码格式,但是它们之间有一些区别:
  • PCM是一种音频信号的编码方式,它将模拟音频信号转换成数字音频信号。而WAV是一种容器格式,它可以将不同的编码格式的音频数据储存起来,比如PCM、MP3等。
  • PCM是无损的编码格式,它将原始音频信号转换为数值型数据储存。而WAV可以支持无损和有损的编码格式。
  • PCM文件只储存音频数据,没有元数据。而WAV文件可以包含音频数据、元数据、文本等。
  • WAV是微软公司开发的一种标准文件格式,所以WAV文件通常在Windows系统上使用较为广泛。而PCM是编码方式,不属于某一种文件格式。
  • WAV文件可以包含多种采样率和位深度的音频数据,而PCM只能够描述一种采样率和位深度的音频数据。

3. 录制音频并保存为WAV文件

在Java中,我们可以使用javax.sound.sampled包提供的API来录制音频。以下是一个完整的示例,演示了如何录制音频并将其保存为WAV文件。

3.1代码示例
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;

public class AudioRecorder {

    // 设置录音的格式
    private static final AudioFormat FORMAT = new AudioFormat(
            AudioFormat.Encoding.PCM_SIGNED, // 编码格式
            16000, // 采样率
            16, // 量化位数
            1, // 声道数
            2, // 每个样本的字节数
            16000, // 每秒的采样数
            true // 是否是大端序
    );

    public static void main(String[] args) {
        File audioFile = new File("recorded.wav");
        try {
            // 创建并启动录音线程
            AudioInputStream audioInputStream = getAudioInput();
            // 将录音数据写入文件
            AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, audioFile);
            System.out.println("录音完成,文件保存为: " + audioFile.getAbsolutePath());
        } catch (IOException | LineUnavailableException e) {
            e.printStackTrace();
        }
    }

    // 录制音频的方法
    private static AudioInputStream getAudioInput() throws LineUnavailableException {
        // 获取音频线(即音频输入源)
        TargetDataLine line = AudioSystem.getTargetDataLine(FORMAT);
        line.open(FORMAT);
        line.start();

        // 创建录音的缓冲区和音频输入流
        byte[] buffer = new byte[4096];
        AudioInputStream audioInputStream = new AudioInputStream(line);

        // 启动录音线程
        Thread recordingThread = new Thread(() -> {
            try {
                System.out.println("开始录音...");
                while (true) {
                    int bytesRead = line.read(buffer, 0, buffer.length);
                    if (bytesRead == -1) break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                line.stop();
                line.close();
            }
        });
        recordingThread.start();

        // 录音持续时间
        try {
            Thread.sleep(5000); // 录制5秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return audioInputStream;
    }
}
3.2 代码解释
  1. 设置录音格式

    AudioFormat类定义了音频数据的格式,包括编码格式、采样率、量化位数、声道数等。
  2. 录制音频

    • 使用AudioSystem.getTargetDataLine(FORMAT)获取一个TargetDataLine对象,它是用来从麦克风或其他音频输入设备读取数据的。
    • 启动TargetDataLine并将其封装在AudioInputStream中。
    • 创建一个录音线程,通过读取TargetDataLine的数据来录制音频。
  3. 保存音频

      使用AudioSystem.write()方法将录制的音频数据保存到WAV文件中。
3.4. 注意事项
  • 录音时间:在示例中,录音时间设置为5秒。你可以根据需要调整录音时间。
  • 异常处理:示例中简单地打印了异常信息。在实际应用中,可能需要更详细的错误处理。
  • 采样率和格式:你可以根据实际需求调整音频格式(如采样率、位深度等)。

4. 录制音频代码实例

下面是一个示例代码,用于录制一段WAV音频并保存到本地文件。

import javax.sound.sampled.*;
/**
 * 
 * @author 赵海洋
 * @date 2020-8-16
 * */
public class AudioRecorder {
    private static final int SAMPLE_SIZE = 16;
    private static final int CHANNELS = 1;
    private static final int SAMPLE_RATE = 44100; 

    public void startRecord(String filename) {
        try {
            AudioFormat format = new AudioFormat(SAMPLE_RATE, SAMPLE_SIZE, CHANNELS, true, true);
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);

            // 不支持当前格式
            if (!AudioSystem.isLineSupported(info)) {
                return;
            }

            TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
            line.open(format);
            line.start();

            AudioInputStream ais = new AudioInputStream(line);
            AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(filename));

            line.stop();
            line.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        AudioRecorder recorder = new AudioRecorder();
        recorder.startRecord("hello.wav");
    }
}

AudioSystem类的isLineSupported()方法来检查当前系统是否支持指定的音频格式。如果不支持,我们可以根据需要选择其他的音频格式。

5. 把音频流保存到本地

在实际应用中特别是语音系统,由于很多语音识别和合成服务都是调用第三方服务,通常提供API或流的形式,这样在实际开发过程中,需要将流保存到本机存储起来。

特别注意:音频流大部分是裸流,没有头部信息,如果保存需要自己手工添加

Java代码示例,演示了如何向一个没有WAV头的裸音频文件中添加WAV头。在这个示例中,我们假设裸音频文件的采样率为24000,声道数为2,每个样本大小为16位。

5.1 纯java代码编写
import java.io.*;
/**
 * 
 * @author 赵海洋
 * @date 2020-8-16
 * */
public class FileAddWavHeader {
    public static void addWavHeader(String inputFilePath, String outputFilePath) {
        try {
            File inputFile = new File(inputFilePath);
            File outputFile = new File(outputFilePath);


            int audioDataLength = (int) inputFile.length();
            int totalDataLength = audioDataLength + 36;

            long mySubChunk1Size = 16;
            int myBitsPerSample= 16;

            long mySampleRate = 24000;
            int myChannel = 1;
            int byteRate = mySampleRate * myChannel * myBitsPerSample/8;
            int myBlockAlign = (int) (myChannel * myBitsPerSample/8);

            int mySubChunk1Size =16;
            int myFormat = 1;
          

            OutputStream os = new FileOutputStream(outputFile);
            DataOutputStream dos = new DataOutputStream(os);

            dos.writeBytes("RIFF");//RIFF
            dos.writeInt(Integer.reverseBytes(totalDataLength));//how big is the rest of this file?
            dos.writeBytes("WAVE");//格式
            dos.writeBytes("fmt ");//fmt
            dos.writeInt(Integer.reverseBytes(mySubChunk1Size));//size of this chunk
            dos.writeShort(Short.reverseBytes((short)myFormat));//what is the audio format? 1 for PCM = Pulse Code Modulation
            dos.writeShort(Short.reverseBytes((short)myChannel));//mono or stereo? 1 or 2
            dos.writeInt(Integer.reverseBytes(mySampleRate));//samples per second (numbers per second)
            dos.writeInt(Integer.reverseBytes(byteRate));//bytes per second
            dos.writeShort(Short.reverseBytes((short)myBlockAlign));//# of bytes in one sample, for all channels
            dos.writeShort(Short.reverseBytes((short) myBitsPerSample));// how many bits in a sample(number)?  usually 16 or 24
            dos.writeBytes("data");
            dos.writeInt(Integer.reverseBytes(audioDataLength));// how big is this data chunk

            FileInputStream fis = new FileInputStream(inputFile);
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                dos.write(buffer, 0, bytesRead);
            }

            fis.close();
            dos.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        addWavHeader("E:\\file.wav", "E:\\out.wav");
    }
}

 

5.2 纯java sound代码实现
import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
import java.io.File;
/**
 * 
 * @author 赵海洋
 * @date 2020-8-16
 * */
public class AudioFormate {
    byte[] bt = null;
    public AudioFormate(byte[] buffer){
        this.bt = buffer;
    }
    public void saveFile(){
        // 设置音频格式和采样率等参数:8000,11025,16000,22050,44100 采样率
        //44100:44.1kHz
        float sampleRate =24000;
        // 8,16 每个样本中的位数
        int sampleSizeInBits = 16;
        // 1,2 信道数(单声道为 1,立体声为 2,等等)
        int channels = 1;
        boolean signed = true;
        // true,false 指示是以 big-endian 顺序还是以 little-endian 顺序存储音频数据。
        boolean bigEndian = false;
        AudioFormat audioFormat = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
        try {
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
            TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
            line.open(audioFormat);
            line.start();
            AudioSystem.write(new AudioInputStream(new ByteArrayInputStream(bt),audioFormat,bt.length/ audioFormat.getFrameSize()),
                    AudioFileFormat.Type.WAVE,
                    new File("D://ss.wav"));

            line.stop();
            line.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

常生果

喜欢我,请支持我

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值