写点什么

啃论文俱乐部——移植 speexdsp 到 OpenHarmony 标准系统⑤

作者:离北况归
  • 2022-10-12
    江苏
  • 本文字数:12345 字

    阅读完需:约 41 分钟

  • 大家好!我来自南京,在 OpenHarmony 成长计划啃论文俱乐部,与华为、软通动力、润和软件、拓维信息、深开鸿等公司一起,学习和研究操作系统技术从今年 1 月 11 日加入 OpenHarmony 俱乐部已经有接近 8 个月时间了。笔者一直在思考啃论文给我带来了些什么,通过啃论文能为 OpenHarmony 做些什么。笔者利用大二升大三暑假两个月时间移植了 Speexdsp 这个三方库到 OpenHarmony 标准系统,而关于前面的问题我似乎找到了答案,现将啃论文和三方库移植分享经验如下:

由于想要分享的内容较多,为避免读者姥爷们失去看下去的耐心,分享将以连载的方式进行。感谢润和软件提供的硬件支持。下期预告:OAT 开源扫描和三方库上仓基本要求


本期为移植speexdsp到OpenHarmony标准系统的第⑤期,主要内容如下:





speexdsp 移植后已提交至 openhamrony sig 仓库https://gitee.com/openharmony-sig/contest/tree/master/2022_OpenHarmony_thirdparty/speexdsp



七、Speexdsp 功能分析

  • 将 speexdsp 加入 openharmony 编译体系后,能成功编译出来动态链接库和测试用的可执行文件,并不代表移植三方库成功。还要在开发板上运行测试其功能是否正常。

speexdsp 核心库分析

1.库实现方式

  • 编程语言:C

  • 原生平台:linux

2.依赖分析

  • 除 C 标准库外,无其他第三方库依赖

3.license 以及版权

  • 根据 speex 官网 https://speex.org/的信息,speexdsp 使用的开源协议为revised BSD license(revised BSD license又称为3-clause BSD License或者BSD 3-Clause License,一般使用 BSD 3-Clause)

  • OpenHamorny 第三方开源软件许可证类型必须是OSI明确定义的。

  • OSI明确定义的开源许可证有:https://opensource.org/licenses/alphabetical

  • BSD 3-Clause LicenseOSI明确定义的开源许可。


如下类型许可证可以引入到 OpenHarmony 项目中:


Apache License 2.0Mulan Permissive Software License, Version 2BSD 2-clauseBSD 3-clauseDOM4J LicensePostgreSQL LicenseEclipse Distribution License 1.0MITISCICUUniversity of Illinois/NCSAW3C Software Licensezlib/libpngAcademic Free License 3.0Python Software Foundation LicensePython Imaging Library Software LicenseBoost Software License Version 1.0WTF Public LicenseUNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWAREZope Public License 2.0
复制代码


如下类型许可证不建议引入到 OpenHarmony 项目中:


GNU GPL 1, 2, 3GNU Affero GPL 3GNU LGPL 2, 2.1, 3QPLSleepycat LicenseServer Side Public License (SSPL) version 1Code Project Open License (CPOL)BSD-4-Clause/BSD-4-Clause (University of California-Specific)Facebook BSD+Patents licenseNPL 1.0/NPL 1.1The Solipsistic Eclipse Public LicenseThe "Don't Be A Dick" Public LicenseJSON LicenseBinary Code License (BCL)Intel Simplified Software LicenseJSR-275 LicenseMicrosoft Limited Public LicenseAmazon Software License (ASL)Java SDK for Satori RTM licenseRedis Source Available License (RSAL)Booz Allen Public LicenseCreative Commons Non-CommercialSun Community Source License 3.0Common Development and Distribution Licenses: CDDL 1.0 and CDDL 1.1Common Public License: CPL 1.0Eclipse Public License: EPL 1.0IBM Public License: IPL 1.0Mozilla Public Licenses: MPL 1.0, MPL 1.1, and MPL 2.0Sun Public License: SPL 1.0Open Software License 3.0Erlang Public LicenseUnRAR LicenseSIL Open Font LicenseUbuntu Font License Version 1.0IPA Font License Agreement v1.0Ruby LicenseEclipse Public License 2.0: EPL 2.0
复制代码


speexdsp 的 license 以及版权内容如下:


Copyright 2002-2008   Xiph.org FoundationCopyright 2002-2008   Jean-Marc ValinCopyright 2005-2007  Analog Devices Inc.Copyright 2005-2008  Commonwealth Scientific and Industrial Research                        Organisation (CSIRO)Copyright 1993, 2002, 2006 David RoweCopyright 2003     EpicGamesCopyright 1992-1994  Jutta Degener, Carsten Bormann
Redistribution and use in source and binary forms, with or withoutmodification, are permitted provided that the following conditionsare met:
- Redistributions of source code must retain the above copyrightnotice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyrightnotice, this list of conditions and the following disclaimer in thedocumentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of itscontributors may be used to endorse or promote products derived fromthis software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOTLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FORA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION ORCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, ORPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OFLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDINGNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
复制代码

4.最新一次版本

  • 2022 年 6 月 16 日,版本号 1.2.1

5.代码规模

linux 系统提供了 wc 命令来统计文件的行数,统计当前目录下的所有文件行数,终端输入如下命令:


wc -l *
复制代码


功能分析

可以参考 speexdsp 提供的文档分析功能

预处理器

预处理器被设计为在运行编码器之前在音频上使用。预处理器提供三个主要功能:


  • 噪声抑制

  • 自动增益控制(AGC)

  • 语音活动检测(V AD)

自适应抖动缓冲区

  • 当通过 UDP 或 RTP 传输语音(或任何相关内容)时,包可能会丢失,以不同的延迟到达,甚至乱序。

  • 抖动缓冲区的目的是重新排序数据包,并缓冲足够长的时间,以便它们可以被发送以进行解码。

声学回声消除器

回声消除是为了提高远端质量。


  • 在任何免提通信系统中,远端语音通过本地扬声器播放。音频在房间内传播,并被麦克风捕获。如果从麦克风捕获的音频被直接发送到远端,那么用户就会听到远端语音的回声。声学回声消除器设计用于在声学回声发送到远端之前消除它。


重采样器

这个重采样器可以用于在任意两个速率之间进行转换(比率必须是有理数),并且可以控制质量/复杂性的权衡。


  • 重采样器在某些情况下将音频从一个采样率转换到另一个采样率。

  • 它可以用于混合具有不同采样率的流、用于支持声卡不支持的采样率、用于转码等。

八、Speexdsp 功能测试

测试逻辑

  • 在 speexdsp 原生库的 libspeexdsp 目录下有原生的测试源文件 testresample.c、testresample2.c、testecho.c、testdenoise.c、testjitter.c。


  • 进行功能测试需要对比 pc 端开发板的运行效果,因此需要在 pc 端编译出测试用的可执行文件(开发板上的已经编译出来了)。


重采样功能

  • 查看源码可知,运行测试重采样率的可执行文件时,输入一份音频文件的同时需要指定处理后输出的音频文件。

  • 测试重采样功能的源文件为 testresample.c 和 testresample2.c。


testresample.c 源码分析如下:


#ifdef HAVE_CONFIG_H#include "config.h"#endif
//要使用重采样器,必须包含它的头文件speex_resampler.h:#include "speex/speex_resampler.h"//基本输入输出函数的声明#include <stdio.h>////头文件<math.h>中声明了一些数学函数和宏#include <math.h>
/*stdlib.h中,包含了C语言的一些常用库函数。如动态内存相关的malloc, realloc,zalloc,calloc,free等。随机数相关的rand,srand等。系统相关的system, getenv,setenv等。字符串转数值函数,atoi, atof,strtoul等。如果在代码中,调用了这个头文件中的函数或者宏定义,则需引用该头文件。*/#include <stdlib.h>
//NN定义的是帧长度#define NN 256
int main(){ spx_uint32_t i; short *in; short *out; float *fin, *fout; int count = 0;
//对于每一个被重采样的流(流是一种抽象的概念,表示一连串的数据元素;流中的数据元素称为帧Frame。),有必要创建一个重采样状态:SpeexResamplerState *resampler;//resampler = speex_resampler_init(nb_channels, input_rate, output_rate, quality, &err);// nb_channels声道数量,这段测试程序要求输入的音频声道数量为1// input_rate和output_rate指的是输入音频的采样率和输出音频的采样率。// err错误码/* quality 是语音编解码器方面的概念,范围是0~10.通常, 对于一般使用质量为3是可以接受的, 而对于专业音频工作,质量为10是最推荐的。 质量0通常具有良好的声音(当然比使用线性插值重采样更好),但可能会听到伪影。 *//*在数字音频领域,常用的采样率有: 8000 Hz - 电话所用采样率, 对于人的说话已经足够 11025 Hz - 电话所用采样率 22050 Hz - 无线电广播所用采样率 32000 Hz - miniDV 数码视频 camcorder、DAT (LP mode)所用采样率 44100 Hz - 音频 CD, 也常用于 MPEG-1 音频(VCD,SVCD,MP3)所用采样率 47250 Hz - 商用 PCM 录音机所用采样率 48000 Hz - miniDV、数字电视、DVD、DAT、电影和专业音频所用的数字声音所用采样率 50000 Hz - 商用数字录音机所用采样率*/ SpeexResamplerState *st = speex_resampler_init(1, 8000, 12000, 10, NULL);
//96000是输入音频的采样率,44100是输出音频的采样率。单位为Hz speex_resampler_set_rate(st, 96000, 44100);
speex_resampler_skip_zeros(st);
/* malloc函数的作用是让系统寻找空闲地址后为你分配一块指定大小的空间来给你使用, 这一块空间其实要比你申请的空间大一些,它包含两个部分:1.你申请的空间;2.记录这个空间信息的空间。*/ in = malloc(NN*sizeof(short)); out = malloc(2*NN*sizeof(short)); fin = malloc(NN*sizeof(float)); fout = malloc(2*NN*sizeof(float));
//while(1):这是一个死循环,一直在while里循环.//调试代码时,为了检测一部分代码是否OK,可加测试点while(1),测试这段代码。 while (1) { spx_uint32_t in_len; spx_uint32_t out_len;/*从终端获取音频数据*/ fread(in, sizeof(short), NN, stdin);
//feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0 if (feof(stdin)) break; for (i=0;i<NN;i++) fin[i]=in[i]; in_len = NN; out_len = 2*NN; /*if (count==2) speex_resampler_set_quality(st, 10);*/
//实际的重采样是使用speex_resampler_process_int(resampler, channelID, in, &in_length, out, &out_length);//其中"channelID"是输入的音频的声道。对于单声道,使用0。也可以同时处理多个通道。//其中"in"指针指向所选通道的输入缓冲区的第一个样本,输出指向输出的第一个样本。//in_length和out_length分别指定输入和输出缓冲区的大小。 speex_resampler_process_float(st, 0, fin, &in_len, fout, &out_len); for (i=0;i<out_len;i++) out[i]=floor(.5+fout[i]); /*speex_warning_int("writing", out_len);*/
/*转换后的数据输入到终端显示*/ fwrite(out, sizeof(short), out_len, stdout); count++; } speex_resampler_destroy(st);
//free函数接受一个指针,然后将指针指向的地址还给系统。(但它不会改变指针的指向,所以一般在free之后还要将指针置 空,不然你的这个指针就会变成野指针)//一般来说,malloc函数应该和free函数成对出现,防止向系统要的内存太多系统不高兴。 free(in); free(out); free(fin); free(fout); return 0;}
复制代码


可以得知:


  • 输入音频的采样率要求为 96000Hz、声道为单声道。

  • 输出音频的采样率为 44100。

回声消除功能

  • 运行测试回声消除的可执行文件时,需要输入两段音频文件,分别为一份麦克风的音频、一份 speaker 的音频。另外需要指定一份处理后输出的音频文件。采样率都为 8000Hz

  • 测试回声消除功能的源文件为 testecho.c。

  • testecho.c 源码分析如下:


/* Speex 源码中附带的这个例子,只适合于串行的链式媒体流,当媒体播放、媒体采集、媒体网络数据接口分属在不同现成时,就会存在同步问题,异步线程会导致信号延迟加大,回声消除收敛效果不好。*/
#ifdef HAVE_CONFIG_H#include "config.h"#endif
/*使用回声消除器需要加#include "speex/speex_echo.h"*/#include "speex/speex_echo.h"/*使用预处理器需要添加#include "speex/speex_preprocess.h"*/#include "speex/speex_preprocess.h"#include <stdio.h>//stdlib 头文件即standard library标准库头文件 #include <stdlib.h>// sys/types.h中文名称为基本系统 数据类型。在应用程序源文件中包含 <sys/types.h> 以访问 _LP64 和 _ILP32 的定义#include <sys/types.h> //stat.h头文件,轻松获取文件属性#include <sys/stat.h> //fcntl.h相关函数 open,fcntl,shutdown,unlink,fclose#include <fcntl.h>
//NN是frame_size帧长度,TAIL是filter_length滤波器长度#define NN 128#define TAIL 1024
//当你需要程序带参数地启动的时候,就用int main(int argc, char *argv[])int main(int argc, char **argv) {//FILE* 指针作为文件句柄,是文件访问的唯一标识,它由fopen函数创建,fopen打开文件成功,则返回一个有效的FILE*指针,否则返回空指针NULL //[nʌl] FILE *echo_fd, *ref_fd, *e_fd; short echo_buf[NN], ref_buf[NN], e_buf[NN]; SpeexEchoState *st; SpeexPreprocessState *den;//采样率为8000.单位为hz int sampleRate = 8000;
if (argc != 4) { fprintf(stderr, "testecho mic_signal.sw speaker_signal.sw output.sw\n"); exit(1); }
// fopen函数用于打开文件 echo_fd = fopen(argv[2], "rb"); ref_fd = fopen(argv[1], "rb"); e_fd = fopen(argv[3], "wb");
/*1.创建一个回声消除状态:SpeexEchoState *echo_state = speex_echo_state_init(frame_size, filter_length);前边的语句SpeexEchoState *st; 2.NN是frame_size帧长度,TAIL是filter_length滤波器长度*/· st = speex_echo_state_init(NN, TAIL); den = speex_preprocess_state_init(NN, sampleRate); speex_echo_ctl(st, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);/*如果你想进一步减少信号中的回波,你可以通过将回波消除器与预处理器相关联来实现.这是通过调用:speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE,echo_state);*/ speex_preprocess_ctl(den, SPEEX_PREPROCESS_SET_ECHO_STATE, st); while (!feof(ref_fd) && !feof(echo_fd)) { fread(ref_buf, sizeof(short), NN, ref_fd); fread(echo_buf, sizeof(short), NN, echo_fd);/*创建回声消除器状态后,可以通过以下方式处理音频: speex_echo_cancellation(echo_state, input_frame, echo_frame, output_frame); 其中,input_frame是麦克风捕捉到的音频,echo_frame是扬声器播放的信号(需要去除),output_frame是去除回声的信号。*/ speex_echo_cancellation(st, ref_buf, echo_buf, e_buf); speex_preprocess_run(den, e_buf); fwrite(e_buf, sizeof(short), NN, e_fd); } speex_echo_state_destroy(st); //可以用以下方法消除回声抵消状态:speex_echo_state_destroy(echo_state); speex_preprocess_state_destroy(den);// fclose(FILE *fp) 一般地,fclose(fp)应与fopen配对使用,特别是含有写方式的文件,若不关闭,可能会造成文件数据丢失。 fclose(e_fd); fclose(echo_fd); fclose(ref_fd); return 0;}
复制代码

预处理功能

  • 运行测试预处理功能的可执行文件时,输入一份音频文件的同时需要指定处理后输出的音频文件。(采样率为 8000Hz)

  • 测试源文件 testdenoise.c 测试的是预处理功能中的噪声抑制。


testdenoise.c 源码分析如下:


#ifdef HAVE_CONFIG_H#include "config.h"#endif//使用预处理功能先#include "speex/speex_preprocess.h"#include "speex/speex_preprocess.h"#include <stdio.h>//定义NN为160#define NN 160
int main(){ short in[NN]; int i;/*然后,可以创建一个预处理器状态:SpeexPreprocessState *preprocess_state = speex_preprocess_state_init(frame_size,sampling_rate);preprocess_state(预处理状态)frame_size指的是帧长度sampling_rate指的是采样率*/ SpeexPreprocessState *st; int count=0; float f;
/*frame_size(帧长度)为160,采样率为8000*/ st = speex_preprocess_state_init(NN, 8000);
/*预处理器的行为可以通过以下方式改变:speex_preprocess_ctl(preprocess_state, request, ptr);*/
/*SPEEX_PREPROCESS_SET_DENOISE Turns denoising on(1) or off(2) (spx_int32_t)*/ i=1; // i=1就是打开预处理器的降噪功能;i=2就是关闭降噪功能 speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DENOISE, &i);
/*SPEEX_PREPROCESS_SET_AGC Turns automatic gain control (AGC) on(1) or off(2) (spx_int32_t)*//*AGC全称是自动增益控制,作用是针对不同的强度的信号使用不同的增益进行放大,使得信号最终的输出幅度维持在同一标准*/ i=0; // speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC, &i);
i=8000; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC_LEVEL, &i);
/*SPEEX_PREPROCESS_SET_DEREVERB Turns reverberation removal on(1) or off(2) (spx_int32_t)*///reverberation removal混响消除 i=0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB, &i);
f=.0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
f=.0; speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);
/* while(1):这是一个死循环,一直在while里循环,调试代码时,为了检测一部分代码是否OK,可加测试点while(1),测试这段代码*/ while (1) { int vad; /*C 库函数 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从给定流 stream 读取数据到 ptr 所指向的数组中。*/ fread(in, sizeof(short), NN, stdin);/*feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0一般在文件操作,中经常使用feof()判断文件是否结束。*/ if (feof(stdin)) break;/*对于每个输入帧,需要调用:speex_preprocess_run(preprocess_state, audio_frame);其中audio_frame被用作输入和输出。*///fwrite和fread是以记录为单位的I/O函数,fread和fwrite函数一般用于二进制文件的输入输出。 vad = speex_preprocess_run(st, in); /*fprintf (stderr, "%d\n", vad);*/ fwrite(in, sizeof(short), NN, stdout); count++; }//预处理器状态可以通过以下方式销毁:speex_preprocess_state_destroy(preprocess_state); speex_preprocess_state_destroy(st); return 0;}
复制代码

抖动缓冲功能

当通过 UDP 或 RTP 传输语音(或任何相关内容)时,包可能会丢失,以不同的延迟到达,甚至乱序。


1.抖动缓冲区的目的是重新排序数据包,并缓冲足够长的时间,以便它们可以被发送以进行解码。


2.测试源文件 testjitter.c,这个去抖动测试需要接收来自 udp/rtp 的网络语音数据,原生测试程序没有做到真正意义上的测试,从程序上看是告诉用户如何使用接口。


testjitter.c 源码分析:


#ifdef HAVE_CONFIG_H#include "config.h"#endif
/*jitter buffer可以通过以下方式启用:#include <speex/speex_jitter.h>*/#include "speex/speex_jitter.h"#include <stdio.h>
union jbpdata { /*联合类型的声明,union是联合体关键字;联合是一种特殊的自定义类型,该种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间,所以联合体也被称为共用体。抖动缓冲器是一个共享的数据区域*/ unsigned int idx; //unsigned int是整数类型 ,存储大小为2 或 4 字节,值范围为0 到 65,535 或 0 到 4,294,967,295//unsigned char的表示范围为0~255 此处定义了一个拥有四个元素的数组 unsigned char data[4];};
void synthIn(JitterBufferPacket *in, int idx, int span) { union jbpdata d; d.idx = idx;
in->data = d.data; in->len = sizeof(d); in->timestamp = idx * 10; in->span = span * 10; in->sequence = idx; in->user_data = 0;}
void jitterFill(JitterBuffer *jb) { char buffer[65536]; JitterBufferPacket in, out; int i;
out.data = buffer;
jitter_buffer_reset(jb);
for(i=0;i<100;++i) { synthIn(&in, i, 1); jitter_buffer_put(jb, &in);
out.len = 65536;
/* 当解码器准备解码数据包时,需要解码的数据包可以通过以下方式获得: int start_offset; err = jitter_buffer_get(state, &packet, desired_span, &start_offset); */ //如果从不同的线程调用jitter_buffer_put()和jitter_buffer_get(),那么需要使用互斥锁来保护jitter缓冲区状态。 if (jitter_buffer_get(jb, &out, 10, NULL) != JITTER_BUFFER_OK) { printf("Fill test failed iteration %d\n", i); } if (out.timestamp != i * 10) { printf("Fill test expected %d got %d\n", i*10, out.timestamp); }/*因为jitter缓冲区被设计为不使用显式计时器,所以需要显式地告诉它时间。这是通过调用:jitter_buffer_tick(state);*/ jitter_buffer_tick(jb); }}
/*当一个包到达时,它需要通过以下方式插入到jitter buffer中:JitterBufferPacket packet;jitter_buffer_put(state, &packet);*/int main(){ char buffer[65536]; JitterBufferPacket in, out; int i;
/* 新的抖动缓冲状态可以通过以下方式初始化:JitterBuffer *state = jitter_buffer_init(step); step参数是用于调整延迟的默认时间步长还有做隐藏,step参数的值总是为1,也可以是更高的值。 */
JitterBuffer *jb = jitter_buffer_init(10);
out.data = buffer;
/* Frozen sender case */ jitterFill(jb); for(i=0;i<100;++i) { out.len = 65536; jitter_buffer_get(jb, &out, 10, NULL); jitter_buffer_tick(jb); } synthIn(&in, 100, 1); jitter_buffer_put(jb, &in); out.len = 65536; if (jitter_buffer_get(jb, &out, 10, NULL) != JITTER_BUFFER_OK) { printf("Failed frozen sender resynchronize\n"); } else { printf("Frozen sender: Jitter %d\n", out.timestamp - 100*10); } return 0;}
复制代码

测试步骤

编译 pc 端上的测试用的可执行程序


编译 pc 端的可执行程序:


gcc testdenoise.c -L /home/jiajiahao/Desktop/speexdsp-SpeexDSP-1.2.1/build/lib -o testdenoise -I /home/jiajiahao/Desktop/speexdsp-SpeexDSP-1.2.1/include -lm -lspeexdsp
复制代码



其中:


  • gcc 后面是测试用的源文件

  • -L 后指定 so 库所在的文件夹

  • -l+so 库的名字(除去 lib 和后缀)

  • -o 后面是可执行文件的名字,

  • -l 后面是测试用的源文件要用到的头文件的路径

将编译生成的库以及测试用的可执行文件推送到开发板上

  • hdc 工具的使用 请参考用hdc工具在OpenHarmony3.2 上安装应用中的相关内容

  • 获取了 ohos 源码,也可以执行./build.sh --product-name ohos-sdk编译 sdk。

  • 如果有这样的编译报错,说明依赖的库没在 linux 上安装完整。

  • 安转完整才能成功编译。


sudo apt-get update sudo apt-get install binutils binutils-dev git git- lfs gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g+±multilib gcc-arm-linux-gnueabi libc6-dev-i386 libc6-dev-amd64 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip m4 bc gnutls-bin python3.8 python3-pip ruby genext2fs device-tree-compiler make libffi-dev e2fsprogs pkg-config perl openssl libssl-dev libelf-dev libdwarf-dev u-boot-tools mtd-utils cpio doxygen liblz4-tool openjdk-8-jre gcc g++ texinfo dosfstools mtools default-jre default-jdk libncurses5 apt-utils wget scons python3.8-distutils tar rsync git-core libxml2-dev lib32z-dev grsync xxd libglib2.0-dev libpixman-1-dev kmod jfsutils reiserfsprogs xfsprogs squashfs-tools pcmciautils quota ppp libtinfo-dev libtinfo5 libncurses5-dev libncursesw5 libstdc++6 gcc-arm-none-eabi vim ssh locales
复制代码


1.通过与 ohos 版本匹配的 hdc_std 工具,将编译生成的库以及测试用的可执行文件推送到开发板上


hdc_std shell               mount -o remount,rw /      ## 重新加载系统为可读写mkdir speexdsp              ## 创建speexdsp目录存放测试用例exit                        ## 退出开发板系统
复制代码


2.将压缩包 push 到开发板


hdc_std file send speexdsp.tar /speexdsp
复制代码


3.解压压缩包并将库文件拷贝到对应的目录


本次移植是基于 openharmony 标准系统 3.2Beta1 版本,是 arm64 位系统


hdc_std shell              ## 进入开发板系统cd speexdsptar -xvf speexdsp.tarcp libspeexdsp_share.z.so /system/lib64/ ## 将库文件拷贝系统目录
复制代码

执行测试程序、分析测试结果

分析测试结果需要使用到的音频处理软件是ocenaudio,下载地址:https://www.ocenaudio.com/en/



①执行 testresample 可执行文件通过分析 testresample.c 源码可知:执行测试程序时输入一份采样率为 96000Hz 并且为单声道的音频时,经过重采样输出的音频采样率为 44100Hz。在 pc 端运行:输入的音频为 input.pcm,把它拷贝到 testresample 同目录下,并且新建空白文档命名为 output.pcm。



打开终端执行如下命令:


chmod 777 testresample./testresample < input.pcm > output.pcm
复制代码


使用重定向<符号指定输入文件、>符号指定输出文件



输出的 output.pcm 采样率变为 44100Hz,音频的波形图声谱图如下:



在 rk3568 上运行


这里测试 testresample 时,将一份与 pc 端同样的 input.pcm 和 output.pcm 拷贝至开发板 speexdsp 目录。在开发板 speexdsp 目录执行语句如下


chmod 777 testresample                  ## 添加可执行权限./testresample < input.pcm > output.pcm
复制代码



将开发板输出的 output.pcm 拷贝至 pc 端


hdc_std file recv /speexdsp/output.pcm C:\Users\jjh\Desktop\
复制代码



测试结果:经过重采样,pc 端和 rk3568 开发板输出的 output.pcm 音频采样率都为 44100Hz,两者运行测试程序 testresample 结果一致。


②执行 testresample2 可执行文件


通过分析 testresample2.c 源码可知,执行测试程序时需要指定一份空白的单声道音频文件 output.pcm。


测试 testresample2 时,需要把空白的单声道音频文件 output.pcm 拷贝至开发板 speexdsp 目录。pc 端运行:执行语句如下:


./testresample2 > output.pcm
复制代码


输出结果如下:终端打印信息



输出音频 output.pcm 波形图和声谱图如下:



rk3568 开发板上运行:执行语句如下:


./testresample2 > output.pcm
复制代码



测试结果:输出一份不为空的 output.pcm 音频文件,并且在终端输出文本如下:


1000 0 1024 22 -> 1024 0                  1100 0 1024 24 -> 1024 151200 0 1024 26 -> 926 261300 926 1122 31 -> 1048 311400 950 1098 33 -> 1032 33.........127600 1024 1024 2723 -> 1024 2723......127900 1024 1024 2729 -> 1024 2729128000 1024 1024 2731 -> 1024 2730
复制代码


以最后一行"128000 1024 1024 2731 -> 1024 2730“”为例,其中 128000 为采样率(Hz);1024 为一个编码单元采样点数(帧);1024 为输入音频理论帧长;2731 为输出音频理论帧长。"->"符号后的 1024 为经过重采样处理输入音频实际帧长,2730 为输出音频实际帧长。


  • pc 端和 rk3568 开发板运行 testresample2 可执行程序效果一致。


③执行 testecho 可执行文件


测试 testecho 时,需要输入两份音频文件,同时需要指定一份输出的音频文件。


输入的两份音频一份为 speaker.wav(麦克风收录的说话人语音信号+在房间多径反射的语音),另一份为 micin.wav(麦克风收录的房间多径反射的语音)。


  • speaker.wav 正常房间环境下收录说话人说话声音即可,mic2.pcm 在正常环境收录时说话人不说话即可。

  • 同时需要指定一份 testecho_output.wav 输出文件。


执行语句如下:


./testecho speaker.wav micin.wav testecho_output.wav
复制代码



测试结果:对比输入的 speaker.wav 和输出 testecho_output.wav 的波形图和声谱图,回声已经被消除。pc 端和 rk3568 开发板运行 testecho 可执行程序效果一致。



④执行 testdenoise 可执行文件


通过分析 testdenoise.c 源码,执行测试程序时需要指定一份输入的不为空的 8000Hz 的 input.pcm 音频,并且需要指定一份空的输出的 output.pcm 音频。


rk3568 上运行:执行语句如下:


./testdenoise < input.pcm > output.pcm 
复制代码



测试结果:对比输入的 input.pcm 和输出的 outpu.pcm 的波形图和声谱图,噪声已经被消除。pc 端和 rk3568 开发板运行 testdenoise 可执行程序效果一致。



⑤执行 testjitter 可执行文件


通过分析 testjitter.c 源码,测试需要接收来自 udp/rtp 的网络语音数据,原生测试程序没有做到真正意义上的测试,从程序上看是告诉用户如何使用接口。


执行语句如下


./testjitter
复制代码


测试结果:终端打印出语句


Frozen sender: Jitter 0
复制代码


pc 端和 rk3568 开发板运行 testjitter 可执行程序效果一致。


下期预告:OAT 开源扫描和三方库上仓基本要求

发布于: 2022-10-12阅读数: 3
用户头像

离北况归

关注

还未添加个人签名 2022-03-26 加入

OpenHarmony啃论文俱乐部PIMF团队。 位于南京一学生,可私信。

评论

发布
暂无评论
啃论文俱乐部——移植speexdsp到OpenHarmony标准系统⑤_OpenHarmony_离北况归_InfoQ写作社区