02. Using fmod to achieve QQ sound change effect

Keywords: Android Gradle cmake Java

introduce

fmod is a very excellent c + + open source framework, this time through this framework to achieve QQ-like sound effects, sound effects of six kinds, normal, Laurie, uncle, thriller, funny, empty. As for more effects, I am interested in exploring them again. The routine is roughly the same.

Project Effect Diagram and Its Principle

Project adoption Fmod Open Source Library, a very simple and universal audio engine, can change the sound effect by processing the original sound. Here is the processing of the variable sound frequency.

  • Sound: Play audio files directly
  • Laurie: Eight degrees for audio
  • Uncle: Reduce audio by eight degrees
  • Thriller: Increase the Vibration of Audio
  • Funny: Increase the speed of audio playback
  • Echo: Increase Audio Echo

Function

If you could run the last fmod sample program in Android studio, there would be no big problem in this run.
The tool used for this time is android studio 2.3.3.

step

1. download fmod

To fmod Official website Register and download the Android version of fmod. Decompression. As follows

This time, the files under inc and lib in api/lowlevel package are used

2. New Android Project

Create a new Android project through as, for example, this time my project name is QQVoice_change
Then the armeabi, x86 files and their. so libraries under the lib package of api/lowlevel are copied to the libs directory of the project.

Copy the inc file and its file under the api/lowlevel package to the cpp directory of the project, and then complete the copy as shown in the figure.

Don't forget to compile fmod.jar into the project after copying it.

3. Modify build.gradle and MakeLists. TXT

Many people have died in compiling c/c++ file dynamic libraries in android studio, which requires gradle and maker programming to really understand. If you make a mistake in this step, you will find many mistakes in a compilation.

Modify Builde.gradle

1. Suppose ndk {} is not configured in build.gradle. Then, when compiled, the compiler will compile the so libraries of all platforms. Whereas only so files of armeabi and x86 are imported here, so when compiling so files of other platforms, so files of other platforms will not be found and errors will be reported. So you need to configure ndk {} in build.gradle.
If you do not configure ndk {} in build.gradle, you need to import so libraries for all platforms.

In this example, only so files of armeabi and x86 are imported, which will cause an error if compiled directly. So add the following code to build.gradle:

ndk { 
    abiFilters "armeabi","x86" 
}

2. The so file is configured under the libs folder here, so it should be configured in build.gradle:

    sourceSets.main {
        jniLibs.srcDirs = ['libs']
        jni.srcDirs = []
    }

The modified build.gradle is as follows

android {
    compileSdkVersion 26
    buildToolsVersion "27.0.3"
    defaultConfig {
        applicationId "com.gxl.change"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
        ndk {
            abiFilters "armeabi","x86"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
    sourceSets.main {
        jniLibs.srcDirs = ['libs']
        jni.srcDirs = []
    }
}

Modify CMakeLists.txt

Because fmod.so and fmodL.so are needed for compilation here, the CPP code for muting sound is placed in native-lib.cpp, and the muting sound is generated. so is called changeVoice, so CMakeLists.txt is as follows

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#-----------------------------------------
find_library( log-lib
              log )

set(my_lib_path ${CMAKE_SOURCE_DIR}/libs)

# Add tripartite so Libraries
add_library(libfmod
            SHARED
            IMPORTED )

 # The Absolute Path of Naming Third Party Library
 set_target_properties( libfmod
                        PROPERTIES IMPORTED_LOCATION
                        ${my_lib_path}/${ANDROID_ABI}/libfmod.so )

 add_library(libfmodL
             SHARED
             IMPORTED )

 set_target_properties( libfmodL
                        PROPERTIES IMPORTED_LOCATION
                        ${my_lib_path}/${ANDROID_ABI}/libfmodL.so )

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

#--------------------------------
add_library( # Sets the name of the library.
             changeVoice

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.



# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

#---------------------
# Import path, so that the folder can be found at compile time
include_directories(src/main/cpp/inc)

# Link three paths
target_link_libraries( changeVoice
                       libfmod
                       libfmodL

                       ${log-lib} )

You can look at this file more. After reading this file more, you will find that many c/c++ libraries run roughly the same way under as.

4. Achieving the function of changing sound

Create a new EffectUtils.java file to handle the murmur. The code is as follows

package com.gxl.change;

/**
 * Created by Administrator on 2018/7/10/010.
 */

public class EffectUtils {

    //Sound effect types
    public static final int MODE_NORMAL = 0;//normal
    public static final int MODE_LUOLI = 1;//Lolita
    public static final int MODE_DASHU = 2;//uncle
    public static final int MODE_JINGSONG = 3;//Thriller
    public static final int MODE_GAOGUAI = 4;//Funny
    public static final int MODE_KONGLING = 5;//Emptiness

    /**
     * Change of voice
     * @param path Voice path
     * @param type Types of voice change
     */
    public static native void fix(String path,int type);

    static
    {
        System.loadLibrary("fmodL");
        System.loadLibrary("fmod");
        System.loadLibrary("changeVoice");
    }
}

Header files can be automatically generated to native-lib.cpp in Android studio version 2.2 or above.

5. Implementing Functions by c++.

Implementing logic in native-lib

#include <jni.h>
#include "inc/fmod.hpp"
#include <string>

#include <unistd.h>

using namespace FMOD;

#define MODE_NORMAL 0
#define MODE_LUOLI 1
#define MODE_DASHU 2
#define MODE_JINGSONG 3
#define MODE_GAOGUAI 4
#define MODE_KONGLING 5

#include <android/log.h>

#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"jason",FORMAT,##__VA_ARGS__);


extern "C"
JNIEXPORT void JNICALL
Java_com_gxl_change_EffectUtils_fix(JNIEnv *env, jclass cls, jstring path_str, jint type) {
    //Many of them are second-level pointers, but here you can define first-level pointers, and here you can achieve the same effect by reference.
    System * system;
    Sound * sound;
    Channel  *channel;
    DSP *dsp;
    bool playing= true;
    float frequency=1;

    //Initialization
    System_Create(&system);
    system->init(32, FMOD_INIT_NORMAL, NULL);

    //Convert string to char*
    const char* path=env->GetStringUTFChars(path_str,NULL);

    //Create voice
    system->createSound(path,FMOD_DEFAULT, 0, &sound);
    try {
        //Change the sound according to the type
        switch (type) {
            case MODE_NORMAL:
                //Normal voice
                system->playSound(sound, 0, false, &channel);
                break;
            case MODE_LUOLI:
                //Lolita
                //DSP digital signal process
                //DSP - > Sound effects create predefined sound effects in fmod
                //FMOD_DSP_TYPE_PITCHSHIFT dsp, a sound effect that promotes or reduces voice calls
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                //Improving sound effects
                dsp->setParameterFloat(FMOD_DSP_TYPE_PITCHSHIFT, 2.5);
                //Play sound
                system->playSound(sound, 0, false, &channel);
                //Add channel to dsp
                channel->addDSP(0, dsp);
                break;
            case MODE_DASHU:
                //uncle
                system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp);
                //decrease sound
                dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 0.8);
                //Play sound
                system->playSound(sound, 0, false, &channel);
                //Add channel to dsp
                channel->addDSP(0, dsp);
                break;
            case MODE_JINGSONG:
                //Thriller
                system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &dsp);
                dsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, 0.5);
                system->playSound(sound, 0, false, &channel);
                channel->addDSP(0, dsp);
                break;
            case MODE_GAOGUAI:
                //Funny
                //Speak faster
                system->playSound(sound, 0, false, &channel);
                //frequency's original sound speed
                channel->getFrequency(&frequency);
                frequency = frequency * 1.6;
                channel->setFrequency(frequency);
                LOGI("%s", "fix gaoguai");
                break;
            case MODE_KONGLING:
                //Emptiness
                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);
                LOGI("%s", "fix kongling");
                break;
            default:
                break;
        }
    }
    catch (...){
        //Catching anomalies
        LOGE("%s","exception occurred");
    }
    system->update();
    //Release resources
    //The unit is microseconds.
    //Decide whether or not to play every second
    while(playing){
        channel->isPlaying(&playing);
        usleep(1000 * 1000);
    }

    //release
    sound->release();
    system->close();
    system->release();
    env->ReleaseStringUTFChars(path_str,path);
}

The above is about sound processing, you can see that the routine is roughly the same, the most difficult is to change the sound effect adjustment, if you need more sound effects, Baidu. I'm just going to knock out the example that the teacher gave me, and it's not clear how to change it into what kind of sound effect. Maybe you can refer to the examples in the source code. It may be troublesome.

6. Writing Interface Processing

Implementing Click Events in MainActivity.java

public class QQActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FMOD.init(this);
        setContentView(R.layout.activity_main);
    }

    public void mFix1(View btn) {
        Toast.makeText(this, "Change of voice", Toast.LENGTH_SHORT).show();
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "gxl.wav";
        EffectUtils.fix(path, EffectUtils.MODE_NORMAL);
        Log.d("jason", "mFix");
    }
    public void mFix2(View btn) {
        Toast.makeText(this, "Change of voice", Toast.LENGTH_SHORT).show();
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "gxl.wav";
        EffectUtils.fix(path, EffectUtils.MODE_LUOLI);
        Log.d("jason", "mFix");
    }
    public void mFix3(View btn) {
        Toast.makeText(this, "Change of voice", Toast.LENGTH_SHORT).show();
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "gxl.wav";
        EffectUtils.fix(path, EffectUtils.MODE_DASHU);
        Log.d("jason", "mFix");
    }
    public void mFix4(View btn) {
        Toast.makeText(this, "Change of voice", Toast.LENGTH_SHORT).show();
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "gxl.wav";
        EffectUtils.fix(path, EffectUtils.MODE_JINGSONG);
        Log.d("jason", "mFix");
    }
    public void mFix5(View btn) {
        Toast.makeText(this, "Change of voice", Toast.LENGTH_SHORT).show();
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "gxl.wav";
        EffectUtils.fix(path, EffectUtils.MODE_GAOGUAI);
        Log.d("jason", "mFix");
    }
    public void mFix6(View btn) {
        Toast.makeText(this, "Change of voice", Toast.LENGTH_SHORT).show();
        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "gxl.wav";
        EffectUtils.fix(path, EffectUtils.MODE_KONGLING);
        Log.d("jason", "mFix");
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        FMOD.close();
    }

}

The interface is as follows
activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical"
        android:background="#FFF"
        android:paddingBottom="20dp" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="20dp">

            <LinearLayout 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <ImageView
                    android:id="@+id/btn_record"
                    style="@style/AudioImgStyle"
                    android:src="@drawable/record"
                    android:onClick="mFix1"/>
                <TextView 
                    style="@style/AudioTextStyle"
                    android:text="Original voice"/>
            </LinearLayout>

            <LinearLayout 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <ImageView
                    android:id="@+id/btn_luoli"
                    style="@style/AudioImgStyle"
                    android:src="@drawable/luoli"
                    android:onClick="mFix2"/>
                <TextView 
                    style="@style/AudioTextStyle"
                    android:text="Lolita"/>
            </LinearLayout>


            <LinearLayout 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <ImageView
                    android:id="@+id/btn_dashu"
                    style="@style/AudioImgStyle"
                    android:src="@drawable/dashu"
                    android:onClick="mFix3"/>
                <TextView 
                    style="@style/AudioTextStyle"
                    android:text="uncle"/>
            </LinearLayout>
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="20dp">

            <LinearLayout 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <ImageView
                    android:id="@+id/btn_jingsong"
                    style="@style/AudioImgStyle"
                    android:src="@drawable/jingsong"
                    android:onClick="mFix4"/>
                <TextView 
                    style="@style/AudioTextStyle"
                    android:text="Thriller"/>
            </LinearLayout>

            <LinearLayout 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <ImageView
                    android:id="@+id/btn_gaoguai"
                    style="@style/AudioImgStyle"
                    android:src="@drawable/gaoguai"
                    android:onClick="mFix5"/>
                <TextView 
                    style="@style/AudioTextStyle"
                    android:text="Funny"/>
            </LinearLayout>

            <LinearLayout 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <ImageView
                    android:id="@+id/btn_kongling"
                    style="@style/AudioImgStyle"
                    android:src="@drawable/kongling" 
                    android:onClick="mFix6"/>
                <TextView 
                    style="@style/AudioTextStyle"
                    android:text="Emptiness"/>
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>


</RelativeLayout>

Posted by FUEL on Thu, 09 May 2019 23:54:39 -0700