关于pcm头部加44个字节转为wav格式的方法,请参照我前面写的博文。
关于soundtouc的下载,整理和修改方面的内容请参考iOS那篇博文,这里不再赘述了,其实只要将那个目录下的文件直接拷过来就可以用了。
下面重点介绍一下如何用NDK编译soundtouch。
1. NDK的相关知识,请查看相关资料,如果有时间我会详细介绍。
2. 在你的工程根目录下,新建jni目录,然后将制作好的SoundTouch拷贝到jni下:
3. 找到STTypes.h头文件,将
#define ST_NO_EXCEPTION_HANDLING 1 原先的注释去掉,即启用这个宏定义,这样就不会使用c++的stdexcep相关的东西了,否则是编译不过去的,如下:
4. 编写Android.mk文件
5. 我们看到上面的Android.mk文件中有一个soundtouch_wapper.cpp,这个是用来连接java和本地c++的一个桥梁,即需要在这个文件中对底层的soundtouch调用进行封装。我们先编写java文件JNISoundTouch.java:
package com.example.soundtouchdemo;
publicclass JNISoundTouch {
publicnativevoid setSampleRate(int sampleRate);
publicnativevoid setChannels(int channel);
publicnativevoid setTempoChange(float newTempo);
publicnativevoid setPitchSemiTones(int newPitch);
publicnativevoid setRateChange(float newRate);
publicnativevoid putSamples(short[] samples, int len);
publicnativeshort[] receiveSamples();
static{
System.loadLibrary("soundtouch");
}
}
这里声明了很多native方法,表示是本地实现的,然后加载了一个叫着“soundtouch”的动态库。
6. 用javah 生成JNISoundTouch.java对应的JNI头文件,先cd到你工程的bin/class目录下
hejinlai_iMac:classes hejinlai$ pwd
/Users/hejinlai/Workspace/Android/SoundTouchDemo/bin/classes
hejinlai_iMac:classes hejinlai$
执行javah com.example.soundtouchdemo.JNISoundTouch生成对应的JNI头文件
hejinlai_iMac:classes hejinlai$ javah com.example.soundtouchdemo.JNISoundTouch
hejinlai_iMac:classes hejinlai$ ls -l
total 8
drwxr-xr-x 3 hejinlai staff 102 6 19 11:32 com
-rw-r--r-- 1 hejinlai staff 1904 6 19 18:45 com_example_soundtouchdemo_JNISoundTouch.h
hejinlai_iMac:classes hejinlai$
7. 将生成的头文件的内容全部复制到soundtouch_wapper.cpp中,然后这个文件中对头文件生成的方法进行实现:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_example_soundtouchdemo_JNISoundTouch */
#ifndef _Included_com_example_soundtouchdemo_JNISoundTouch
#define _Included_com_example_soundtouchdemo_JNISoundTouch
#ifdef __cplusplus
extern"C" {
#endif
#include"SoundTouch/SoundTouch.h"
#define BUFFER_SIZE 4096
soundtouch::SoundTouch mSoundTouch;
/*
* Class: com_example_soundtouchdemo_JNISoundTouch
* Method: setSampleRate
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_example_soundtouchdemo_JNISoundTouch_setSampleRate
(JNIEnv *env, jobject obj, jint sampleRate)
{
mSoundTouch.setSampleRate(sampleRate);
}
/*
* Class: com_example_soundtouchdemo_JNISoundTouch
* Method: setChannels
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_example_soundtouchdemo_JNISoundTouch_setChannels
(JNIEnv *env, jobject obj, jint channel)
{
mSoundTouch.setChannels(channel);
}
/*
* Class: com_example_soundtouchdemo_JNISoundTouch
* Method: setTempoChange
* Signature: (F)V
*/
JNIEXPORT void JNICALL Java_com_example_soundtouchdemo_JNISoundTouch_setTempoChange
(JNIEnv *env, jobject obj, jfloat newTempo)
{
mSoundTouch.setTempoChange(newTempo);
}
/*
* Class: com_example_soundtouchdemo_JNISoundTouch
* Method: setPitchSemiTones
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_example_soundtouchdemo_JNISoundTouch_setPitchSemiTones
(JNIEnv *env, jobject obj, jint pitch)
{
mSoundTouch.setPitchSemiTones(pitch);
}
/*
* Class: com_example_soundtouchdemo_JNISoundTouch
* Method: setRateChange
* Signature: (F)V
*/
JNIEXPORT void JNICALL Java_com_example_soundtouchdemo_JNISoundTouch_setRateChange
(JNIEnv *env, jobject obj, jfloat newRate)
{
mSoundTouch.setRateChange(newRate);
}
/*
* Class: com_example_soundtouchdemo_JNISoundTouch
* Method: putSamples
* Signature: ([SI)V
*/
JNIEXPORT void JNICALL Java_com_example_soundtouchdemo_JNISoundTouch_putSamples
(JNIEnv *env, jobject obj, jshortArray samples, jint len)
{
// 转换为本地数组
jshort *input_samples = env->GetShortArrayElements(samples, NULL);
mSoundTouch.putSamples(input_samples, len);
// 释放本地数组(避免内存泄露)
env->ReleaseShortArrayElements(samples, input_samples, 0);
}
/*
* Class: com_example_soundtouchdemo_JNISoundTouch
* Method: receiveSamples
* Signature: ([SI)I
*/
JNIEXPORT jshortArray JNICALL Java_com_example_soundtouchdemo_JNISoundTouch_receiveSamples
(JNIEnv *env, jobject obj)
{
short buffer[BUFFER_SIZE];
int nSamples = mSoundTouch.receiveSamples(buffer, BUFFER_SIZE);
// 局部引用,创建一个short数组
jshortArray receiveSamples = env->NewShortArray(nSamples);
// 给short数组设置值
env->SetShortArrayRegion(receiveSamples, 0, nSamples, buffer);
return receiveSamples;
}
#ifdef __cplusplus
}
#endif
#endif
这里设计到很多JNI的相关知识,如果读者不是很了解,可以查找相应的资料。
8. 在命令行中,cd到所在工程的jni目录下,调用ndk-build进行编译生成动态库so文件:
hejinlai_iMac:jni hejinlai$ pwd
/Users/hejinlai/Workspace/Android/SoundTouchDemo/jni
hejinlai_iMac:jni hejinlai$ ndk-build
/Users/hejinlai/Android/android-ndk-r8c/build/core/add-application.mk:128: Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in /Users/hejinlai/Workspace/Android/SoundTouchDemo/AndroidManifest.xml
Compile++ thumb : soundtouch <= AAFilter.cpp
Compile++ thumb : soundtouch <= BPMDetect.cpp
Compile++ thumb : soundtouch <= cpu_detect_x86.cpp
Compile++ thumb : soundtouch <= FIFOSampleBuffer.cpp
Compile++ thumb : soundtouch <= FIRFilter.cpp
Compile++ thumb : soundtouch <= mmx_optimized.cpp
Compile++ thumb : soundtouch <= PeakFinder.cpp
Compile++ thumb : soundtouch <= RateTransposer.cpp
SharedLibrary : libsoundtouch.so
Install : libsoundtouch.so => libs/armeabi/libsoundtouch.so
hejinlai_iMac:jni hejinlai$
9. 设置soundtouch的相关参数:
soundtouch.setSampleRate(16000);
soundtouch.setChannels(1);
soundtouch.setPitchSemiTones(10);
soundtouch.setRateChange(-0.7f);
soundtouch.setTempoChange(0.5f);
10. 调用soundtouch.putSamples方法将录音数据传递给soundtouch处理,然后在调用
soundtouch.receiveSamples方法得到处理后的数据
11. 给处理后的数据加上44个字节头部,转为wav格式,然后保存到sdcrad中:
WaveHeader header = new WaveHeader(fileLength);
byte[] headers = header.getHeader();
// 保存文件
FileOutputStream out = new FileOutputStream(Settings.recordingPath + "soundtouch.wav");
out.write(headers);
for(byte[] bytes: wavDatas){
out.write(bytes);
}
out.close();
主要的步骤就是这些,这个运行的效果和iOS那个版本是差不多的,大家可以参考。
我把这个工程的源代码上传到附件中了,欢迎大家下载。