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

前言

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

  • 场景
    在这里插入图片描述

1. pcm wav 转化工具类

入参和出参都为byte[],理论上有了 byte[] 就可以输出为文件,或者用于网络交互。
在这里插入图片描述

  • 浏览器播放的短音频,区分一下声道数、采样率即可。
  • 讯飞api文档中 audio/L16;rate=8000 表示单声道8000的采样率
package com.james.convert;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 CHANNELS = 1;/*** 采样率*/private static final Integer RATE = 8000;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(RATE,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 * RATE);writeShort(bos, (short) (CHANNELS * 2) * RATE);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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/32057.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于单片机的智能窗户控制系统的设计

摘 要&#xff1a; 根据单片机技术和现代传感器技术 &#xff0c; 本文主要针对基于单片机的智能窗户控制系统的设计进行探讨 &#xff0c; 仅供参考 。 关键词&#xff1a; 单片机 &#xff1b; 智能窗户 &#xff1b; 控制系统 &#xff1b; 设计 在现代科学技术持续发展的带…

面完小红书算法岗,心态崩了。。。

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。提前准备才是完全之策。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c…

Python爬虫基础以及示例讲解

爬虫简介 网络爬虫 爬虫指在使用程序模拟浏览器向服务端发出网络请求&#xff0c;以便获取服务端返回的内容。 但这些内容可能涉及到一些机密信息&#xff0c;所以爬虫领域目前来讲是属于灰色领域&#xff0c;切勿违法犯罪。 爬虫本身作为一门技术没有任何问题&#xff0c;关…

Docker之overlay2的迁移

原因 docker默认将文件及其容器放置在了系统盘的挂载区内&#xff0c;如果长期使用会发现系统挂载区被overlay2挤爆了,因此在一开始我们将其迁移在大容量外挂磁盘上,就可以避免系统盘被挤爆,放心使用. 具体操作 # 停止容器 systemctl stop docker# 修改容器配置&#xff0c…

等差数列和等比数列的介绍及在Java编程中的实现

等差数列 (Arithmetic Sequence) 定义&#xff1a;等差数列是指相邻两项的差相等的数列&#xff0c;这个差值叫做“公差”&#xff08;d&#xff09;。 公式&#xff1a; 第 ( n ) 项的公式&#xff1a; 前 ( n ) 项和的公式&#xff1a; 示例&#xff1a; 假设第一项 (…

AI+前端技术的结合(实现图片识别功能)

随着人工智能技术的不断发展&#xff0c;AI在前端设计页面中的应用变得越来越普遍。比如&#xff1a;在电商平台上&#xff0c;可以利用对象检测技术实现商品的自动识别和分类&#xff1b;人脸识别&#xff1b;车辆检测&#xff1b;图片识别等等......其中一个显著的应用是在图…

数据结构_优先级队列(堆)

目录 一、优先级队列 1.1 堆 1.2 PriorityQueue接口 二、模拟实现优先级队列 2.1 初始化 2.2 创建大根堆 (向下调整) 2.3 堆的插入 2.4 堆的删除 2.5 堆排序 总结 一、优先级队列 优先级队列是一种特殊的队列&#xff0c;其出队顺序与入队顺序无关&#xff0c;而与优…

易支付宝塔一键部署项目 懒人专用包 制作

宝塔一键部署说明 https://www.bt.cn/bbs/thread-33063-1-1.html 1. auto_install.json {"php_ext":"fileinfo","chmod":[],"success_url":"install/?step3&jump1","php_versions":"80","db…

利用机器学习弄懂机器学习!

第一个问题&#xff1a;什么是机器学习&#xff1f; 机器学习是人工智能的一个子领域&#xff0c;它使计算机系统能够从经验中学习并改进其性能&#xff08;和人一样&#xff0c;人也是从经验当中&#xff0c;实现自己的不断地学习和提升&#xff09;。简单来说&#xff0c;机…

架构师指南:现代 Datalake 参考架构

这篇文章的缩写版本于 2024 年 3 月 26 日出现在 The New Stack 上。 旨在最大化其数据资产的企业正在采用可扩展、灵活和统一的数据存储和分析方法。这一趋势是由企业架构师推动的&#xff0c;他们的任务是制定符合不断变化的业务需求的基础设施。现代数据湖体系结构通过将数…

逻辑地址 线性地址 物理地址 Linux kernel 内存管理设计

linux kernel 2.6以后的MM&#xff0c;受到了兼容 risc arch cpu 的 MM 的启发&#xff0c;新的 MM 架构对 x86 上任务切换的效率上也有明显提高。 新的MM架构&#xff0c;GDT 不再随着进程的创建与结束而创建和删除 新的表项。 TSS段 也只有一个&#xff0c;进程切换时&…

深度学习入门2—— 神经网络的组成和3层神经网络的实现

由上一章结尾&#xff0c;我们知道神经网络的一个重要性质是它可以自动地从数据中学习到合适的权重参数。接下来会介绍神经网络的概要&#xff0c;然后再结合手写数字识别案例进行介绍。 1.神经网络概要 1.1从感知机到神经网 我们可以用图来表示神经网络&#xff0c;我们把最…

上位机图像处理和嵌入式模块部署(mcu之静态库生成和使用)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 很多同学做了很长时间的mcu和keil开发&#xff0c;都认为keil工程中必须包含所有的源代码&#xff0c;其实这是不对的。如果有一些代码我们不希望别…

IKVM.net调用Jar包实现SM4解密

近期&#xff0c;我深入学习了如何使用IKVM.net来调用Jar包&#xff0c;这次的学习经历让我对Java与.NET之间的互操作性有了更深刻的理解。IKVM.net作为一款强大的工具&#xff0c;为我们打通了Java与.NET之间的桥梁&#xff0c;使得在.NET环境中调用Java库变得简单而高效。 在…

红队内网攻防渗透:内网渗透之内网对抗:信息收集篇自动项目本机导出外部打点域内通讯PillagerBloodHound

红队内网攻防渗透 1. 内网自动化信息收集1.1 本机凭据收集类1.1.1、HackBrowserData 快速获取浏览器的账户密码1.1.2、Searchall 快速搜索服务器中的有关敏感信息还有浏览器的账户密码1.1.3、Pillager 适用于后渗透期间的信息收集工具,可以收集目标机器上敏感信息1.2 对外打点…

密码(6)

一、[NCTF 2019]Keyboard——键盘密码 1.题目&#xff1a; ooo yyy ii w uuu ee uuuu yyy uuuu y w uuu i i rr w i i rr rrr uuuu rrr uuuu t ii uuuu i w u rrr ee www ee yyy eee www w tt ee 2.解题&#xff1a; 这些字母是26键盘上的第一行&#xff0c;每个字母对应上…

redis类型解析汇总

redis类型解析汇总 介绍数据类型简介主要数据类型&#xff1a;衍生类型&#xff1a; 字符串&#xff08;String&#xff09;底层设计原理图例设计优势字符串使用方法设置字符串值获取字符串值获取和设置部分字符串获取字符串长度追加字符串设置新值并返回旧值递增/递减同时设置…

通过 WireGuard 组建虚拟局域网 实现多个局域网全互联

本文后半部分代码框较多,欢迎点击原文链接获得更佳的阅读体验。 前言 上一篇关于 WireGuard 的文章通过 Docker 安装 wg-easy 的形式来使用 WireGuard,但 wg-easy 的功能比较有限,并不能发挥出 WireGuard 的全部功力。 如果只是想要出门在外连随时随地的连回家里的局域网,…

067、Python 高阶函数的编写:优质冒泡排序

以下写了个简单的冒泡排序函数&#xff1a; def bubble_sort(items: list) -> list:for i in range(1, len(items)):swapped Falsefor j in range(0, len(items) - 1):if items[j] > items[j 1]:items[j], items[j 1] items[j 1], items[j]swapped Trueif not swa…

UCOS高频面试题及参考答案(2万字长文)

目录 UCOS-II/UCOS-III的基本特点和适用场景 UCOS-II与UCOS-III的主要区别 UCOS的任务状态 OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()函数 UCOS-III任务调度过程 时间片轮转调度与UCOS-II 创建UCOS任务的步骤 使用UCOS信号量进行任务同步 信号量与互斥量的区别 UCOS中…