前言

微信公众号开发,因为需要在页面发送语音和播放,由于公众号页面中录音必须要调用微信js录音,录音完成由前端上传到微信临时素材,再由后端下载到服务器,然后给前端播放,但是因为从微信下载下来的语音智能是speex格式(高清语音)和amr格式,然而这2种格式都是无法直接在HTML中播放的,所以需要对语音进行转码,由于speex格式清晰度较高,所以我选择了下载speex格式的语音进行转码,本文就是记录如果一步一步调用speex官方源码和微信提供部分C代码进行转码,注:本文所有环境和命令是基于Linux的

下载并安装speex

环境:

Linux Centos

Gcc

JDK 1.8

speex 1.2.0

步骤:

首先下载speex最新的源码,下载地址,解压然后进入源码目录,执行命令

sudo ./configure

验证环境是否有误,如果有问题,则根据具体提示自行安装和配置,如果没有异常,则可以执行命令进行编译安装了

sudo make;sudo make install

如果没有出问题,则会在/usr/local/lib文件夹下面产生libspeex.so等文件,如果有问题,则根据具体提示解决,因为我这里没有遇到任何问题,所以也无法提供常见的问题了

编写Java调用C语言代码

在Java中调用C或者C++的代码技术叫做JNI,是Java原生支持的,首先我们要定义好原生方法的定义,代码如下

package wang.raye.speex;

import java.lang.reflect.Field;

/**

* Speex 转码工具类

* @author Raye

* @since 2017年10月19日17:04:47

*/

public class SpeexUtil {

/**

* .speex to .wav

* @param in .speex文件路径

* @param out .wav文件路径

* @return

*/

public static native boolean decode(String in, String out);

static {

try{

System.load(System.getProperty("user.dir")+java.io.File.separator+"libjspeex.so");

} catch (Exception e) {

e.printStackTrace();

}

}

}

其中

public static native boolean decode(String in, String out);

是定义的原生方法,也就是C语言的方法,而static部分是加载C语言代码的动态库,有2种方法可以加载动态链接库

System.load

System.loadLibrary

其中System.load 参数必须为库文件的绝对路径,可以是任意路径,System.loadLibrary 参数为库文件名,不包含库文件的扩展名,但是库路径必须是在JVM属性java.library.path所指向的路径中,这里我是获取的绝对路径,就是项目目录,因为用的spring boot直接打包的jar运行的,类写好之后生成class文件,然后用Javah命令生成C语言的.h文件,在class文件执行命令

javah -classpath . wang.raye.speex.SpeexUtil

会生成

wang_raye_speex_SpeexUtil.h文件,这就是C语言的头文件,里面定义了我们SpeexUtil定义的decode,当然是没有实现的,具体实现代码需要我们自己实现

wang_raye_speex_SpeexUtil.h 内容:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include

/* Header for class wang_raye_speex_SpeexUtil */

#ifndef _Included_wang_raye_speex_SpeexUtil

#define _Included_wang_raye_speex_SpeexUtil

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class: wang_raye_speex_SpeexUtil

* Method: decode

* Signature: (Ljava/lang/String;Ljava/lang/String;)Z

*/

JNIEXPORT jboolean JNICALL Java_wang_raye_speex_SpeexUtil_decode

(JNIEnv *, jclass, jstring, jstring);

#ifdef __cplusplus

}

#endif

#endif

修改微信demo

具体方法实现可以调用微信的demo,首先下载微信的demo,下载地址

由于微信demo里面是main方法的,所以需要进行修改,原本的SpeexDecode.c代码

#include

#include

#include

#include "TRSpeex.h"

int main(int argc, char* argv[])

{

FILE *fpInput;

FILE *fpOutput;

char aInputBuffer[MAX_FRAME_SIZE*10];

char aOutputBuffer[MAX_FRAME_SIZE*10];

int ret;

int buffer_size;

int nOutSize;

int nPackNo;

TRSpeexDecodeContex SpeexDecode;

int nTotalLen;

char buf[44];

if(argc <3)

{

printf("Usage SpeexDecode InputspxFile OutputWavFile\n");

return 1;

}

memset(aInputBuffer,0,sizeof(char)*MAX_FRAME_SIZE*10);

memset(buf,0,44);

buf[0] = 'R';

buf[1] = 'I';

buf[2] = 'F';

buf[3] = 'F';

buf[8] = 'W';

buf[9] = 'A';

buf[10] = 'V';

buf[11] = 'E';

buf[12] = 'f';

buf[13] = 'm';

buf[14] = 't';

buf[15] = 0x20;

buf[16] = 0x10;

buf[20] = 0x01;

buf[22] = 0x01;

buf[24] = 0x80;

buf[25] = 0x3E;

buf[29]= 0x7D;

buf[32] = 0x02;

buf[34] = 0x10;

buf[36] = 'd';

buf[37] = 'a';

buf[38] = 't';

buf[39] = 'a';

TRSpeexDecodeInit(&SpeexDecode);

fpInput = fopen(argv[1],"rb");

if(fpInput == NULL)

{

printf("can't open input spx file");

return 0;

}

fpOutput = fopen(argv[2],"wb");

if(fpOutput == NULL)

{

printf("can't open output file");

return 0;

}

fwrite(buf,1,44,fpOutput);

nTotalLen = 0;

buffer_size = 6;

ret = fread(aInputBuffer, 1,buffer_size,fpInput);

while(1)

{

TRSpeexDecode(&SpeexDecode,aInputBuffer,buffer_size,aOutputBuffer, &nOutSize);

ret = fread(aInputBuffer, 1,buffer_size, fpInput);

if(ret != buffer_size)

break;

fwrite(aOutputBuffer,1, nOutSize,fpOutput);

nTotalLen += nOutSize;

}

TRSpeexDecodeRelease(&SpeexDecode);

fseek(fpOutput,40,SEEK_SET);

fwrite(&nTotalLen,1,4,fpOutput);

fseek(fpOutput,4,SEEK_SET);

nTotalLen += 36;

fwrite(&nTotalLen,1,4,fpOutput);

fclose(fpOutput);

fclose(fpInput);

return 0;

}

首先需要把方法名称由main方法改为自己想要的名字,这里我改成了decode,其次修改参数,因为Java调用传递的是2个字符串参数,speex的路径和转码后的wav的路径,所以需要先将原来的参数argc删除,并删除

if(argc <3)

{

printf("Usage SpeexDecode InputspxFile OutputWavFile\n");

return 1;

}

同时删除argv参数,添加两个参数char* in ,char* out,分别对应speex的路径和转码后的wav的路径,然后修改代码中的

fpInput = fopen(argv[1],"rb");

fpInput = fopen(in,"rb");

修改

fpOutput = fopen(argv[2],"wb");

fpOutput = fopen(out,"wb");

修改后的代码为

#include

#include

#include

#include "TRSpeex.h"

int decode(char* in,char* out)

{

FILE *fpInput;

FILE *fpOutput;

char aInputBuffer[MAX_FRAME_SIZE*10];

char aOutputBuffer[MAX_FRAME_SIZE*10];

int ret;

int buffer_size;

int nOutSize;

int nPackNo;

TRSpeexDecodeContex SpeexDecode;

int nTotalLen;

char buf[44];

memset(aInputBuffer,0,sizeof(char)*MAX_FRAME_SIZE*10);

memset(buf,0,44);

buf[0] = 'R';

buf[1] = 'I';

buf[2] = 'F';

buf[3] = 'F';

buf[8] = 'W';

buf[9] = 'A';

buf[10] = 'V';

buf[11] = 'E';

buf[12] = 'f';

buf[13] = 'm';

buf[14] = 't';

buf[15] = 0x20;

buf[16] = 0x10;

buf[20] = 0x01;

buf[22] = 0x01;

buf[24] = 0x80;

buf[25] = 0x3E;

buf[29]= 0x7D;

buf[32] = 0x02;

buf[34] = 0x10;

buf[36] = 'd';

buf[37] = 'a';

buf[38] = 't';

buf[39] = 'a';

TRSpeexDecodeInit(&SpeexDecode);

fpInput = fopen(in,"rb");

if(fpInput == NULL)

{

printf("can't open input spx file");

return 0;

}

fpOutput = fopen(out,"wb");

if(fpOutput == NULL)

{

printf("can't open output file");

return 0;

}

fwrite(buf,1,44,fpOutput);

nTotalLen = 0;

buffer_size = 6;

ret = fread(aInputBuffer, 1,buffer_size,fpInput);

while(1)

{

TRSpeexDecode(&SpeexDecode,aInputBuffer,buffer_size,aOutputBuffer, &nOutSize);

ret = fread(aInputBuffer, 1,buffer_size, fpInput);

if(ret != buffer_size)

break;

fwrite(aOutputBuffer,1, nOutSize,fpOutput);

nTotalLen += nOutSize;

}

TRSpeexDecodeRelease(&SpeexDecode);

fseek(fpOutput,40,SEEK_SET);

fwrite(&nTotalLen,1,4,fpOutput);

fseek(fpOutput,4,SEEK_SET);

nTotalLen += 36;

fwrite(&nTotalLen,1,4,fpOutput);

fclose(fpOutput);

fclose(fpInput);

return 0;

}

修改完成后为了方便引用改文件后缀c为h

实现原生方法

微信demo修改后,就可以实现wang_raye_speex_SpeexUtil.h的方法了,新建wang_raye_speex_SpeexUtil.c,编写如下代码

#include "wang_raye_speex_SpeexUtil.h"

#include "SpeexDecode.h"

JNIEXPORT jboolean JNICALL Java_wang_raye_speex_SpeexUtil_decode

(JNIEnv * env, jclass p2, jstring p3, jstring p4)

{

const char *str3 = (*env)->GetStringUTFChars(env, p3, 0);

const char *str4 = (*env)->GetStringUTFChars(env, p4, 0);

return 0==decode(str3,str4);

}

这里就是实现了一个调用中转,在这个方法中调用刚刚修改的SpeexDecode.h的方法

打包so

代码写完后,需要把wangrayespeexSpeexUtil.h和wang_raye_speex_SpeexUtil.c以及修改过的微信demo的代码进行打包成so文件。注:Windows环境下就是DLL文件

首先创建打包文件makefile-linux,编写一下内容

#共享库文件名,lib*.so

TARGET := libjspeex.so

#compile and lib parameter

#编译参数

CC := gcc

LIBS :=-lspeex

LDFLAGS :=

DEFINES :=

INCLUDE := -I. -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux

CFLAGS := -g -Wall -O3 $(DEFINES) $(INCLUDE)

CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H

SHARE := -fPIC -shared -o

#i think you should do anything here

#下面的基本上不需要做任何改动了

#source file

#源文件,自动找所有.c和.cpp文件,并将目标定义为同名.o文件

SOURCE := $(wildcard *.c) $(wildcard *.cpp)

OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))

.PHONY : everything objs clean veryclean rebuild

everything : $(TARGET)

all : $(TARGET)

objs : $(OBJS)

rebuild: veryclean everything

clean :

rm -fr *.o

rm -rf *.so

veryclean : clean

rm -fr $(TARGET)

$(TARGET) : $(OBJS)

$(CC) $(CXXFLAGS) $(SHARE) $@ $(OBJS) $(LDFLAGS) $(LIBS)

rm -rf *.o

install:

rm -rf /usr/local/lib/$(TARGET)

cp $(TARGET) /usr/local/lib

其中libjspeex是动态库的名字,保存后执行命令

sudo make -f makefile-linux

如果没有异常则执行命令

sudo make -f makefile-linux install

完成后会在/usr/local/lib文件夹中生成libjspeex.so文件,如果编译时出现

relocation R_X86_64_32 against `.rodata' can not be used when making a shared object

是由于系统是AMD64位的,所以需要在编译的时候添加 -fPIC 选项,需要修改makefile-linux的CC := gcc行为CC := gcc -fPIC

再重新执行命令即可

使用

将生成的libjspeex.so放到项目根目录即可使用,如果使用时提示

speex.xxxx --cannot open shared object file: No such file or directory,

则是因为/usr/local/lib并没有在系统的环境变量里面,可以修改/etc/ld.so.conf,然后刷新

vi /etc/ld.so.conf

增加一行 include /usr/local/lib

sudo ldconfig

结尾

本文只是记录了我在使用过程中遇到的一些问题,有些没有遇到的欢迎补充,另外如果知道Java如果加载jar中so文件也麻烦告知一下,现在就在头疼这个问题

java speex转码_微信Speex转wav,Speex to wav相关推荐

  1. java微信公众平台接口封装源码_微信公众号对接第三方平台(全网发布 java)源码 含 jar...

    [实例简介] 微信公众号对接第三方平台(全网发布 java)源码+jar 里面有完成全网发布的源码直接tomcat部署运行和所需的jar 具体对接流程,关注我们的博客 http://blog.csdn ...

  2. java selector 源码_基于selector的源码分析和理解、思想和应用实践

    来自京东架构师(JAVA)欢迎关注我的微信公众号java2arch,更多技术文章可看. 一个连接请求connetion request过来,产生一个通道channel(包含并封装了connection ...

  3. java中字节码_聊聊Java的字节码

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 巴山楚水凄凉地,二十三年弃置身. 怀旧空吟闻笛赋,到乡翻似烂柯人. 沉舟侧畔千帆过,病树前头万 ...

  4. java如何获取手机号码_微信小程序+Java获取用户授权手机号码

    前言 小程序内可以直接通过授权获取用户微信号绑定的手机号码或用户添加的其他手机号码,这样可以使得小程序在进行账户的身份可控上又提高了一步,那么应该如何来获取手机号码呢?这篇文章就和大家一起来研究一下. ...

  5. 微信跳一跳java实现自动跳_微信跳一跳自动连跳挂java源码

    火凤下载吧小编为大家带来了微信跳一跳自动连跳挂java源码分享哦,可以快速帮您在游戏中获得高分,而且还不会被系统检查到,是大家在玩微信小游戏跳一跳时候的最佳助手哦,有想要体验的亲们就来下载吧! 微信跳 ...

  6. java弹力球程序_微信小程序 源码下载(弹力球)

    [实例简介] [实例截图] [核心代码] var winWidth = 0 var winHeight = 0 var diameter = 10 var time = 0 Page({ data:{ ...

  7. java实现qq抢红包_微信抢红包到底是怎么抢到的?

    " 微信抢红包功能,一下子拉近了长辈与我们之间的距离,每年都是盼望着亲人群抢红包,今天来给大家分享一下,为啥抢红包有人多,有人少,什么时候抢最合适?" 01 - 微信抢红包 场景描 ...

  8. 小程序向java后台发送图片_微信小程序在后台如何将二进制流转换成图片

    我在前端请求了小程序码返回的是一堆乱码, java不太熟网上找了一个方法可以将二进制流和图片互转,但是从微信小程序码接口获取的数据用这个方法无法获取正确的图片,不知道哪里有问题: 有没有只在前端就能获 ...

  9. java 模板接口开发_微信公众平台 发送模板消息(Java接口开发)

    前言:最近一直再弄微信扫码推送图文消息和模板消息发送,感觉学习到了不少东西.今天先总结一下微信公众平台模板消息的发送.因为这个自己弄了很久,开始很多地方不明白,所以今天好好总结一下. 微信公众平台技术 ...

  10. java商城源码_盘点这些年被黑的最惨的语言,Java瑟瑟发抖

    点击上方"蓝字",关注了解更多 这些年,被黑过的语言数不胜数, 最惨的要数HTML, 但CSS.Python等也惨遭黑手 比惨? 这就来一波. HTML HTML C语言 C语言 ...

最新文章

  1. 使用池来实现并发服务器
  2. (二)python3 只需3小时带你轻松入门——基本变量
  3. Linux Shell脚本专栏_批量检测网站是否异常脚本_08
  4. MIT黑科技:无需视觉输入,立体声音频+摄像机元数据即可实现移动车辆定位
  5. thinkphp5--文章发布后台管理系统
  6. StanfordDB class自学笔记 (2) 关系模型
  7. harview .har文件解析
  8. 在哪一类期刊中发表论文最难,SCI、SSCI、还是AHCI?
  9. 用计算机怎么计算税率,一般纳税人税率计算器,一般纳税人税率如何计算?
  10. CCC与Android交互的注意点
  11. 2020考研计算机(408)考试大纲
  12. Python3.8 新特性:f-strings调试
  13. 大数据技术原理与应用(林子雨)-NoSQL数据库单元测验
  14. 转载 PVE 防火墙
  15. u盘启动 联想一体机_联想一体机如何进入bios设置u盘启动_联想一体机设置U盘启动步骤...
  16. 达人评测 i5 1135g7和i5 11300h的区别
  17. Oracle to_date()函数的用法
  18. USACO 2.1.4 健康的荷斯坦奶牛 Healthy Holsteins
  19. 网站被黑了该如何解决?网站被黑了怎么办
  20. Unity通过相机控制场景模型的移动和缩放

热门文章

  1. TRIZ创新方法#学习笔记02# |功能分析、技术矛盾和物理矛盾
  2. 华为手机连电脑当摄像头用_怎么用华为手机当电脑摄像头
  3. c语言 1 2 3传统流程图,1.3.2 用流程图描述算法
  4. 计算机任务栏的透明颜色设置,教你电脑任务栏透明设置教程
  5. android 静默安装实现,Android 中静默安装实现详解
  6. 记一次联想Y7000P安装黑apple系统地经历
  7. 联想android电视软件下载,#联想智能电视普及风暴#绕开“乐商店”,大叔教你一步步在智能电视上安装第三方APK...
  8. java 解析xml工具类_轻松解析XML的工具类
  9. SLIC超像素算法学习笔记
  10. python交通流预测算法_一种高速公路交通流预测方法与流程