鸿蒙5.0开发【WAV音频编码实现】

音频常见格式

对音频进行编码常见的格式有:

格式类型说明
PCM无压缩一种将模拟信号的数字化方法,无损编码。
WAV无压缩有多种实现方式,但是都不会进行压缩操作。其中一种实现就是在 PCM 数据格式的前面加上 44 字节,分别用来描述 PCM 的采样率、声道数、数据格式等信息。音质非常好,大量软件都支持。
MP3有损压缩音质在 128 Kbps 以上表现还不错,压缩比比较高,大量软件和硬件都支持,兼容性好。
AAC有损压缩在小于 128 Kbps 的码率下表现优异,并且多用于视频中的音频编码。
OPUS有损压缩可以用比 MP3 更小的码率实现比 MP3 更好的音质,高中低码率下均有良好的表现,兼容性不够好,流媒体特性不支持。适用于语音聊天的音频消息场景。

PCM 是音频原始数据的基础格式,并不支持直接用于播放,但可以将其通过编码转换成其他支持播放的格式文件,也可将一些格式文件解码成PCM后再进行编码来实现不同格式的音频文件转换;AAC 则在短视频和直播场景广泛使用。

我们能直接获取到的默认音频格式为音频裸数据格式,即 PCM 格式,因此并不需要对 PCM 进行额外的编码,但需要获取到对应的配置,用于将 PCM 格式的音频转换为其他格式的音频,比如我们本文中的 WAV 格式。

WAV

WAV 全称 Waveform Audio File Format,是微软公司开发的一种无损声音文件格式,也叫波形声音文件,是最早的数字音频格式,被 Windows 平台及其应用程序广泛支持。

WAV 符合 RIFF(Resource Interchange File Format) 规范,所有的WAV都由 44字节 文件头PCM 数据 组成,这个文件头包含语音信号的所有参数信息(声道数、采样率、量化位数、比特率…) 44个字节的 头文件由 3个区块组成:

  • RIFF chunk:WAV文件标识
  • Format chunk: 声道数、采样率、量化位数、等信息
  • Data chunk:存放 PCM 数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

根据上图,所有区块的内容如下:

RIFF 区块

名称字节数内容描述
ID4Byte‘RIFF’RIFF标识
Size4BytefileSize - 8整个文件的长度减去IDSize的长度
Type4Byte‘WAVE’WAVE 格式类型。表示后面需要两个子块:Format区块和Data区块

format 区块

名称字节数内容描述
ID4Byte'fmt ’fmt 标识
Size4Byte16区块长度 fmt (不包含IDSize 部分的长度)
AudioFormat2Byte音频格式PCM = 1(即线性量化),1 以外的值表示一些压缩形式。
NumChannels2Byte声道数音频数据的声道数,1:单声道,2:双声道
SampleRate4Byte采样率音频数据的采样率
ByteRate4Byte每秒数据字节数音频数据的码率:采样率 * 通道数 * 位深 / 8
BlockAlign2Byte数据块对齐一个样本的字节数:通道数 * 位深 / 8
BitsPerSample2Byte采样位数位深:8 位 = 8,16 位 = 16,等等

data 区块

名称字节数内容描述
ID4Byte‘data’data标识
Size4Byte音频数据长度音频数据长度,一般为ByteRate * 时间(s),若为文件,则可直接取文件长度
DataNByte音频数据音频数据内容

根据上述的WAV 格式标准,我们就可以在鸿蒙上实现一个将 PCM 格式文件编码成 WAV 格式文件的功能函数了

实现

WAV 文件格式还是比较清晰的,因此实现上也比较简单,将 PCM 文件的内容读取出来,按照上述的格式,在 WAV 文件中写入文件头,再将 PCM 文件内容续写到 WAV 文件中即可完成 PCM 到 WAV 格式的音频文件转换。

由于鸿蒙 ArkTS 对前端的友好性,对于前端来说,实现上也变得更加简单。

import fs, { ReadOptions } from '@ohos.file.fs';
export class PcmToWavUtil {
  //采样率
  private mSampleRate: number = 0
  // 声道数
  private mChannel: number = 0;
  
  /**
   * @param sampleRate  sample rate、采样率
   * @param channel     channel、声道
   */
  constructor(sampleRate:number, channel: number) {
    this.mSampleRate = sampleRate;
    this.mChannel = channel;
  }

  /**
   * pcm文件转wav文件函数
   * @param src  源文件
   * @param dest 目标文件
   */
  public pcmToWav(src: string, dest: string) {
    const inFile: fs.File = fs.openSync(src, fs.OpenMode.READ_ONLY);
    const outFile: fs.File = fs.openSync(dest, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    let byteRate = 16 * this.mSampleRate * this.mChannel / 8;
    const inFileStat = fs.statSync(inFile.fd)
    // 音频文件
    let audioDataSize = inFileStat.size;
    let totalDataLen = audioDataSize + 36;
    // 1. wav 文件头编写
    this.writeWaveFileHeader(outFile, audioDataSize, totalDataLen, byteRate);
    // 2. 写入 pcm 数据
    this.writePcmData(inFile, outFile, audioDataSize)
  }

  // pcm 数据写入到 pcm 文件函数
  private writePcmData(inFile: fs.File, outFile: fs.File, audioDataSize: number) {
    // 写入 pcm 数据
    let readSize = 0
    let data = new ArrayBuffer(audioDataSize);
    let readOptions: ReadOptions = {
      offset: readSize,
      length: audioDataSize
    };
    let readLen = fs.readSync(inFile.fd, data, readOptions);
    while (readLen > 0) {
      readSize += readLen;
      fs.writeSync(outFile.fd, data, { length: readLen});
      readOptions.offset = readSize;
      readLen = fs.readSync(inFile.fd, data, readOptions);
    }
    fs.closeSync(inFile.fd)
    fs.closeSync(outFile.fd)
  }
  
  //写入wav文件头函数
  private writeWaveFileHeader(
    out: fs.File,
    audioDataSize: number,
    totalDataLen: number,
    byteRate: number
  ) {
    const header = new ArrayBuffer(44);
    const dv = new DataView(header);
    const bitsPerSample = 16; // 当前位深是16

    // 写入RIFF块
    this.writeString(dv, 0, 'RIFF');
    dv.setUint32(4, totalDataLen, true);
    this.writeString(dv, 8, 'WAVE');

    // 写入fmt块
    this.writeString(dv, 12, 'fmt ');
    dv.setUint32(16, 16, true); // fmt块大小
    dv.setUint16(20, 1, true); // 格式类别 (PCM)
    dv.setUint16(22, this.mChannel, true); // 通道数
    dv.setUint32(24, this.mSampleRate, true); // 采样率
    dv.setUint32(28, byteRate, true); // ByteRate 码率
    dv.setUint16(32, this.mChannel * bitsPerSample / 8, true); // BlockAlign
    dv.setUint16(34, bitsPerSample, true); // 位深

    // 写入data块
    this.writeString(dv, 36, 'data');
    dv.setUint32(40, audioDataSize, true); // 数据块大小
    // 写入
    fs.writeSync(out.fd, new Uint8Array(header).buffer, {
      length: 44
    })
  }

  private writeString(dv: DataView, offset: number, str: string) {
    for (let i = 0; i < str.length; i++) {
      dv.setUint8(offset + i, str.charCodeAt(i));
    }
  }
}

最后

如今随着 ffmpeg 的发展,已经可以实现各种音视频的直接转换,能够直接输入一个音视频文件,通过一系列的指令和参数,实现一键粗暴生成想要的音视频格式了。但站在开发者的角度,从很多方面考虑,在能使用ffmpeg的情况下还是很原因使用的,除非场景或需求上由于一些条条框框的限制上不允许,比如这里的 wav 格式编码,可能直接通过代码实现只需要百来行代码,但引入 ffmpeg 这么一个庞然大物是否值得呢。

基于 HarmonyOS NEXT 广泛应用的 ArkTS 语言,众多前端技术得以在鸿蒙系统上顺畅运用。例如,在上述音频编码实现中,DataView 类和 fs 模块的表现与前端中的 DataView 以及 Node 环境下的 fs 模块的使用上高度相似,这使得在功能实现过程中减少了一些技术障碍。

就目前来看,HarmonyOS NEXT 在一定情况下为前端开发者拓展了新的领域方向,提供了更多选择的可能性。


最后呢,很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。

​​​​1

高清完整版请点击《鸿蒙NEXT星河版开发学习文档》

针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细资料鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。

《鸿蒙 (OpenHarmony)开发学习视频》

《鸿蒙生态应用开发V2.0白皮书》

《鸿蒙 (OpenHarmony)开发基础到实战手册》

《鸿蒙开发基础》

《鸿蒙开发进阶》

《鸿蒙开发实战》
在这里插入图片描述

获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》

总结

鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。

并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

  • 19
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值