仿QQ语音变声功能实现

版权声明:转载必须注明本文转自张鹏辉的博客: http://blog.csdn.net/qingtiangg


大家好,这是我从业以来第一篇博客,给大家拜个晚年,祝大家鸡年大吉,幸福美满、事业有成。好了请容许我唠叨几万字。首先自我介绍下
我叫张鹏辉大家可以叫我道长,我是老司······哦不,我是新人请大家多多关照。 一直都是看别人的博客成长(复制、黏贴),总想自己写点什么,发出来,让大家去点评,给建议互相成长。这也是我的初衷,也就有了这个博客的诞生。。。在下目前从事移动开发所以目前以移动端内容来发布,记录一些日常开发中碰到的坑,和项目中一些模块拿出来开源互相学习(几万字略。。。我用意念给你们介绍完了,就当你们了解我了)

闲话不多说了开撸,如题今天请跟贫道一步步来实现球球语音变声功能

分析

首先我们来看下QQ语音变声效果,如下图所示:

这里写图片描述

点击发送语音在点击相应的生效发出去就变声了,没玩过的可以打开QQ试下,那么他们是怎么做到让声音变声呢!看官们接着往下看。

首先我们知道,录制人声到手机,这些音频数据是由二进制十六进制组成,如果我们自己去用算法处理这些数据这个难度是比较大的。

那么怎么办???庆幸吧我们Android开发中有个NDK的东西,简单来说就是我们在java层调用c/c++的一些强大的开源库,(这里不讲NDK基础如果想了解请谷歌一下)。

网上有很多开源的音频处理库。我不知道腾讯是用的那个,但我们今天用到的是FMOD音频引擎,如果你做过游戏开发相信你一定接触过,这里附上FMOD官网链接大家可以去简单的看下介绍,国内FMOD的中文资料几乎没有,所以还是建议你去官网了解,点我进fmod官网,这里就不详细介绍了,他的主要功能就是对音频进行处理,比如你玩赛车类游戏你的引擎轰鸣声,会随着你的速度变化而变化,游戏中有多个视角,比如车内切换到车外,你听到的声音是不是不一样的,再比如我们当年玩的cs游戏,你附近的敌人在你不同角度开枪,你听到的声音也是不一样的,(!!!暴露年龄了。。。)





或许我们来个图更好理解:

这里写图片描述

听到脚步声或枪声,就可以判断出敌人在你什么位置,不同的位置方位明显听到的声音不同

开始

好的大体环境介绍完了,接下来我们看代码。

首先我们新建eclipse工程‘’mysound‘’(这里不要问我为什么不用Android Studio开发as现在多火啊!,因为ndk开发as还是有些问题的,有些坑我也没解决,我能怎么办?我也很绝望呀 。当然了以后我主要还是以as去写,如果今天的项目你能移植到as里面请联系我!)

首先我们导入一下FMOD的so动态库和一些需要用到的头文件。

这里写图片描述

今天的所有代码和用到的库我都会放到github上,所以不用担心这些文件去哪找。

1.接了下我们将工程转成C/C++工程 Add Native Support

这里写图片描述

之后会给你生成android.mk文件 我们来配置一下里面的文件,引入提供的预编译的so库把他们放到jni目录下,还有他自己的函数的实现也要引入看下面

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)
LOCAL_MODULE := fmod
LOCAL_SRC_FILES := libfmod.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := fmodL
LOCAL_SRC_FILES := libfmodL.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE    := sound
LOCAL_SRC_FILES :=effect_fix.cpp
LOCAL_SHARED_LIBRARIES := fmod fmodL
LOCAL_LDLIBS := -llog
LOCAL_CPP_FEATURES :=exceptions
include $(BUILD_SHARED_LIBRARY)

LOCAL_MODULE := sound 自己的动态库名切记要和System.loadLibrary("sound");一致这坑我踩过 ,fmod fmodL这是2个预编译的库
如果你引入的是官网提供的库可能会出现一些路径的问题改一下就好我的已经改了。

划重点 FMOD 用到了标准模板库STL,说用这玩意会变慢?,但我没感觉出来
安卓ndk里面已经有了一个最小的c++运行库,想用那么我们就需要另外的配置

怎么配置呢?看ndk提供的文档 在我们下载的ndk目录里就有解决方法APP_STL

这里写图片描述
这里写图片描述

2.创建Application.mk 支持C++异常处理,标准模板块

里面就一句话 :

APP_STL := gnustl_static

3.配置完我们来写java代码

布局我就不在这贴出来了几个按钮,看关键代码

我们先来个工具类Utils

public class Utils {
        //音效类型 大叔萝莉什么的类型
        public static final int MODE_NORMAL=0;
        public static final int MODE_LUOLI=1;
        public static final int MODE_DASHU=2;
        public static final int MODE_JINGSONG=3;
        public static final int MODE_GAOGUAI=4;
        public static final int MODE_KONGLING=5;
    /**
     * 音效处理传2个值
     * @param path 音频文件路径
     * @param type 音频文件类型
     */
        public native static void fix(String path,int type);
        //加载动态库
        //要和你android.mk文件一致
        static{
                System.loadLibrary("fmodL");
                System.loadLibrary("fmod");
                System.loadLibrary("sound");
        }
}

接下来我们生成头文件

这里写图片描述

然后到项目F5刷新一下src目录下会多出个.h的文件把它拖到jni目录下
继续在jni目录下创建一个c++源文件 supersound.cpp代码

#include "inc/fmod.hpp"
#include <stdlib.h>
#include <unistd.h>
#include  "org_fmod_example_Utils.h"

#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"zph",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"zph",FORMAT,##__VA_ARGS__);

#define MODE_NORMAL 0
#define MODE_LUOLI 1
#define MODE_DASHU 2
#define MODE_JINGSONG 3
#define MODE_GAOGUAI 4
#define MODE_KONGLING 5
using namespace FMOD;

JNIEXPORT void JNICALL Java_org_fmod_example_Utils_fix(JNIEnv *env,
        jclass jcls, jstring path_jstr, jint type) {
    //初始化
    System *system;
    Sound *sound;
    DSP *dsp;
    bool playing = true;
    Channel *channel; 
    float frequency = 0;
    System_Create(&system);
    system->init(32, FMOD_INIT_NORMAL, NULL);//32通道
    const char* path_cstr = env->GetStringUTFChars(path_jstr, NULL);
    try {

        //创建声音
        system->createSound(path_cstr, FMOD_DEFAULT, NULL, &sound);
        switch (type) {
        case MODE_NORMAL:
            //原生播放
            LOGI("%s", path_cstr);
            system->playSound(sound, 0, false, &channel);
            LOGI("%s", "fix normal");
            break;
        case MODE_LUOLI:
            //loli
            //DSP digital singal process
            //dsp->音效
            //dsp 提升或者降低音调的一种音效
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            //设置音调的参数
            dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 8.0);
            //添加进到channel
            system->playSound(sound, 0, false, &channel);
            channel->addDSP(0, dsp);
            break;
        case MODE_DASHU:
            //大叔
            system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
            //设置音调的参数
            dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.8);
            //添加进到channel
            system->playSound(sound, 0, false, &channel);
            channel->addDSP(0, dsp);
            break;
        case MODE_JINGSONG:
            //惊悚
            system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
            dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 5);
            system->playSound(sound, 0, false, &channel);
            channel->addDSP(0, dsp);
            break;
        case MODE_GAOGUAI:
            //搞怪
            //提高说话的速度
            system->playSound(sound, 0, false, &channel);
            channel->getFrequency(&frequency);
            frequency = frequency * 2;
            channel->setFrequency(frequency);
            break;
        case MODE_KONGLING:
            //空灵
            system->createDSPByType(FMOD_DSP_TYPE_ECHO, &dsp);
            dsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, 300);
            dsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, 20);
            system->playSound(sound, 0, false, &channel);
            channel->addDSP(0, dsp);
            break;
        default:
            break;
        }
    } catch (...) {
        LOGE("%s", "发生异常");
        goto end;
    }
    system->update();
    //释放资源
    //单位是微妙
    //每秒钟判断下是否是播放
    while (playing) {
        channel->isPlaying(&playing);
        usleep(1000 * 1000);
    }
    goto end;
    end: env->ReleaseStringUTFChars(path_jstr, path_cstr);
    sound->release();
    system->close();
    system->release();
}

举个例子比如在我们播放一段mp3的时候里面又人声,乐器声等,如果我想对其中一个钢琴的声音加一个特效

这里写图片描述

那么怎么加呢!这就用到它了Channel *channel; 一般是32个通道修改完以后混音

好了到这里关键代码已经写完,我们可以测试玩玩了.

项目源码下载地址:

https://github.com/CN-ZPH/MySound.git
觉得不错请点一个star蛤!
有问题下面留言评论,我看到会回复。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,030评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,198评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,995评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,973评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,869评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,766评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,967评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,599评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,886评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,901评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,728评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,504评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,967评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,128评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,445评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,018评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,224评论 2 339

推荐阅读更多精彩内容