您的位置:首页 > 财经 > 金融 > 【Java】字节数组 pcm 与 wav 格式互转 (附原理概述)

【Java】字节数组 pcm 与 wav 格式互转 (附原理概述)

2024/11/17 3:06:32 来源:https://blog.csdn.net/chenghan_yang/article/details/139881216  浏览:    关键词:【Java】字节数组 pcm 与 wav 格式互转 (附原理概述)

前言

最近实现了一个文字转语音的功能,语音引擎返回的是pcm格式的数据。需要转化成wav格式前端才能播放。本文首先会给出解决方案,后续会讲背后的原理。

  • 场景
    在这里插入图片描述
  • git 仓库
    https://github.com/ChenghanY/pcm-wav-converter

1. pcm wav 转化工具类

入参和出参都为byte[],理论上有了 byte[] 就可以输出为文件,或者用于网络交互。
输出为文件的部分可以看 【Java】pcm 与 wav 格式互转工具类 (附测试用例)
在这里插入图片描述

  • 浏览器播放的短音频,区分一下声道数、采样率即可。
  • 讯飞api文档中 audio/L16;rate=8000 表示单声道8000的采样率
package com.james;import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;public class AudioFormatConverter {/*** 采样率*/private static final Integer RATE = 8000;/*** 声道*/private static final Integer CHANNELS = 1;public static byte[] pcmToWav(byte[] pcmBytes) {return addHeader(pcmBytes, buildHeader(pcmBytes.length));}public static byte[] wavToPcm(byte[] wavBytes) {return removeHeader(changeFormatToWav(wavBytes));}private static byte[] addHeader(byte[] pcmBytes, byte[] headerBytes) {byte[] result = new byte[44 + pcmBytes.length];System.arraycopy(headerBytes, 0, result, 0, 44);System.arraycopy(pcmBytes, 0, result, 44, pcmBytes.length);return result;}private static byte[] changeFormatToWav(byte[] audioFileContent) {AudioFormat format = new AudioFormat(8_000,16,CHANNELS,true,false);try (final AudioInputStream originalAudioStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(audioFileContent));final AudioInputStream formattedAudioStream = AudioSystem.getAudioInputStream(format, originalAudioStream);final AudioInputStream lengthAddedAudioStream = new AudioInputStream(formattedAudioStream, format, audioFileContent.length);final ByteArrayOutputStream convertedOutputStream = new ByteArrayOutputStream()) {AudioSystem.write(lengthAddedAudioStream, AudioFileFormat.Type.WAVE, convertedOutputStream);return convertedOutputStream.toByteArray();} catch (UnsupportedAudioFileException | IOException e) {throw new RuntimeException(e);}}private static byte[] removeHeader(byte[] audioFileContent) {return Arrays.copyOfRange(audioFileContent, 44, audioFileContent.length);}private static byte[] buildHeader(Integer dataLength) {try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {writeChar(bos, new char[]{'R', 'I', 'F', 'F'});writeInt(bos, dataLength + (44 - 8));writeChar(bos, new char[]{'W', 'A', 'V', 'E'});writeChar(bos, new char[]{'f', 'm', 't', ' '});writeInt(bos, 16);writeShort(bos, 0x0001);writeShort(bos, CHANNELS);writeInt(bos, AudioFormatConverter.RATE);writeInt(bos, (short) (CHANNELS * 2) * RATE);writeShort(bos, (short) (CHANNELS * 2));writeShort(bos, 16);writeChar(bos, new char[]{'d', 'a', 't', 'a'});writeInt(bos, dataLength);return bos.toByteArray();} catch (IOException e) {throw new RuntimeException(e);}}private static void writeShort(ByteArrayOutputStream bos, int s) throws IOException {byte[] arr = new byte[2];arr[1] = (byte) ((s << 16) >> 24);arr[0] = (byte) ((s << 24) >> 24);bos.write(arr);}private static void writeInt(ByteArrayOutputStream bos, int n) throws IOException {byte[] buf = new byte[4];buf[3] = (byte) (n >> 24);buf[2] = (byte) ((n << 8) >> 24);buf[1] = (byte) ((n << 16) >> 24);buf[0] = (byte) ((n << 24) >> 24);bos.write(buf);}private static void writeChar(ByteArrayOutputStream bos, char[] id) {for (char c : id) {bos.write(c);}}
}

2. 原理概述

在这里插入图片描述

wav格式实际上就是在pcm数据上加了头部,让浏览器能够解析pcm数据,进而能播放音频。可以类比 TCP协议的报文头,报文头携带了数据长度、偏移量等元信息。

3. 重回代码

根据原理概述,把网上的代码重构了一下,明确语义后的形式,也就是上文的两个方法。

    public static byte[] pcmToWav(byte[] pcmBytes) {return addHeader(pcmBytes, buildHeader(pcmBytes.length));}public static byte[] wavToPcm(byte[] wavBytes) {return removeHeader(changeFormatToWav(wavBytes));}

后记

把一些测试资源放上来,后续整合到仓库中,提供完整的测试用例:

  1. 音频文件的下载地址
    https://samplelib.com/zh/sample-wav.html
    https://support.huaweicloud.com/sdkreference-sis/sis_05_0039.html

  2. pcm转mp3,播放后用于验证pcm文件的正确性
    https://www.yayapeiyin.com/pcm-to-mp3/

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com