音频格式转换,Java中这样写

内容分享11小时前发布
0 0 0

在有些时候,我们可能需要将音频的pcm裸流转换为可播放的格式。列如,AI语音播报场景中,算法生成并返回的语音流就需要转换为WAV、MP3等格式来在应用端播放。

我们看看在服务端如何做音频格式转换。

WAV格式

转换成WAV格式相对简单,我们直接看代码

public static byte[] convertPcmToWav(byte[] pcmData, int sampleRate, int channels, int bitDepth) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    // RIFF header
    writeString(baos, "RIFF");
    writeInt(baos, 36 + pcmData.length); // Chunk size
    writeString(baos, "WAVE");

    // fmt chunk
    writeString(baos, "fmt ");
    writeInt(baos, 16); // Subchunk1Size
    writeShort(baos, (short1); // Audio Format (PCM)
    writeShort(baos, (short) channels);
    writeInt(baos, sampleRate);
    writeInt(baos, sampleRate * channels * (bitDepth / 8)); // Byte Rate
    writeShort(baos, (short) (channels * (bitDepth / 8))); // Block Align
    writeShort(baos, (short) bitDepth);

    // data chunk
    writeString(baos, "data");
    writeInt(baos, pcmData.length);
    baos.write(pcmData);

    return baos.toByteArray();
}

private static void writeString(ByteArrayOutputStream baos, String s) throws IOException {
    for (byte b : s.getBytes()) {
        baos.write(b);
    }
}

private static void writeInt(ByteArrayOutputStream baos, int i) throws IOException {
    baos.write(i & 0xFF);
    baos.write((i >> 8) & 0xFF);
    baos.write((i >> 16) & 0xFF);
    baos.write((i >> 24) & 0xFF);
}

private static void writeShort(ByteArrayOutputStream baos, short s) throws IOException {
    baos.write(s & 0xFF);
    baos.write((s >> 8) & 0xFF);
}

pcmData是输入的字节流,后面的参数需要和数据提供方确认,列如24k采样率,16bit存储,默认单声道,那么就可以像下面这样调用

private byte[] wavData = convertPcmToWav(inputStream, 24000, 1, 16);

这样,我们就可以通过WebSocket等方式推送给前端来播放音频。

MP3格式

MP3格式要麻烦些,我们借助ffmpeg工具库。

第一导入依赖

<!-- 导入ffmpeg依赖-->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>ffmpeg</artifactId>
    <version>${ffmpeg.version}-${javacv.version}</version>
</dependency>
<!--linux平台导入ffmpeg依赖 -->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>ffmpeg</artifactId>
    <version>${ffmpeg.version}-${javacv.version}</version>
    <!--该标签标识了引入ffmpeg的平台版本,这是由于ffmpeg由c语言编写,在不同平台上的编译结果不同-->
    <classifier>linux-x86_64</classifier>
</dependency>

然后看一下如何使用

public static byte[] convertPcmToMp3(byte[] pcmData, int sampleRate, int channels, int bitsPerSample) throws Exception {
    // Create temporary PCM file
    Path tempPcmPath = Files.createTempFile("temp_pcm"".raw");
    try (FileOutputStream fos = new FileOutputStream(tempPcmPath.toFile())) {
        fos.write(pcmData);
    }

    // Create temporary MP3 file
    Path tempMp3Path = Files.createTempFile("temp_mp3"".mp3");

    // Construct command line arguments
    String[] cmd = {
            "ffmpeg",
            "-y"// Overwrite output files without asking
            "-f""s" + bitsPerSample + "le"// Sample format (little-endian)
            "-ar", String.valueOf(sampleRate), // Sample rate
            "-ac", String.valueOf(channels), // Number of audio channels
            "-i", tempPcmPath.toString(),
            "-b:a""128k"// Bitrate
            tempMp3Path.toString()
    };

    // Execute FFmpeg command
    ProcessBuilder processBuilder = new ProcessBuilder(cmd);
    Process process = processBuilder.start();

    // Wait for the process to complete
    int exitCode = process.waitFor();
    if (exitCode != 0) {
        throw new RuntimeException("FFmpeg conversion failed.");
    }

    // Read the MP3 data into a byte array
    byte[] mp3Data = Files.readAllBytes(tempMp3Path);

    // Delete temporary files
    Files.deleteIfExists(tempPcmPath);
    Files.deleteIfExists(tempMp3Path);

    return mp3Data;
}

用法与前面的类似,不过这里需要用到临时文件,我不太喜爱这种做法,似乎也有直接转换的方式,就没有试过了。

一般来说,掌握这两种常见的音频转换就够用了。实则ffmpeg是一个强劲的工具,不光是音频,视频也可以转换,列如目前很火的AI虚拟数字人,就可能需要用到视频相关的了,感兴趣可以学习一下。

© 版权声明

相关文章

暂无评论

none
暂无评论...