您的位置:首页 > 移动开发 > Android开发

Android下使用SoundTouch实现变声并转为wav格式播放(山寨汤姆猫)

2013-06-19 18:57 495 查看
关于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<jni.h>/* Header for class com_example_soundtouchdemo_JNISoundTouch */#ifndef _Included_com_example_soundtouchdemo_JNISoundTouch#define _Included_com_example_soundtouchdemo_JNISoundTouch#ifdef __cplusplusextern"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那个版本是差不多的,大家可以参考。
我把这个工程的源代码上传到附件中了,欢迎大家下载。

本文出自 “移动开发” 博客,请务必保留此出处http://ikinglai.blog.51cto.com/6220785/1225309
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: