0. Official documents

It is recommended that students with strong self-study ability read the official documents directly

  • FMOD SDK Android platform access document description
  • If you are not familiar with NDK, check out the NDK: Google official NDK Documentation (to climb the wall, bring your own ladder 🪜)
  • CMAKELIST syntax: links

1. Download

1.1 download

Log in to the official FMOD website, open the download address, select the corresponding platform and version, process: FMOD Studio Suit -> FMOD Engine select the version, platform select Android

1.2 Directory Level Brief

Unzip and go to fmodStudioAPI {version number}/ API /core/ directory

  • Example: provides two kinds of NDK compilation methods cmake, NDKBuild various functions Demo
  • Inc: Secondary CPP source files provided by FMOD
  • Lib: fmod.jar and the dynamic SO library for different ABI architectures, import as needed

If you want to see what each Demo does, you can open the app/ SRC /main/ CPP /{project name}. CPP file and check the DOC description at the top of the file


2. Import voice changing Demoeffects

Dem path: / fmodstudioapi20104android/API/core/examples/androidstudio/cmake/effects

Personally, starting from the official Demo is the best way to get started.

Be careful not to pull out the Demo directory separately, as it contains some library files that are used in external directories.

This Demo is very simple, showing roughly four kinds of flange effects, lowpass, Highpass, Echo, etc.

At the same time, you can learn how to compile NDK in Cmake mode in Demo. If problems occur in configuring NDK in the future, you can refer to Demo to modify the NDK.


3. Create a project

3.1 Creating a Project

  • Select the template when creating a new projectNative C++

3.2 Import relevant SO library

Open the unzipped directory: fmodstudioapi20104android/API/core /

  • willincandlibImport the corresponding directory as shown below:

  • The configuration selects the supported ABI schema

Open the build.gradle file in the app directory and configure the NDK architecture

defaultConfig { ... NDK {abiFilters "x86", "armeabi-v7A "," arm64-v8A" In general, these three ABI architectures are sufficient to represent all Android models currently on the market}}Copy the code
  • configurationCMakeLists.txtfile

To facilitate configuration, I moved the file to the following directory hierarchy:

Of course, after moving, you need to modify the path of CMakeLists in the build.gradle at the project level:

android{ ... // Specify the cmakelists. TXT path and version number. The default path is the same as build.gradle. ExternalNativeBuild {cmake {path "cmakelists. TXT" version "3.10.2"}}}Copy the code

Detailed configuration of cmakelists.txt

The configuration of CMakeLists is complicated, so it is suggested to learn the basic grammar first

Pay particular attention to the project path where the file is located, which affects the path in the configuration

After the configuration is complete, run it first to see if there is any problem before proceeding to other steps

Cmake_minimum_required (VERSION 3.4.1) # ${CMAKE_SOURCE_DIR} ## ${ANDROID_ABI} This refers to the abi set(FMOD_LIB_DIR) supported in build.gragle My_source file(GLOB my_source SRC /main/ CPP /*.cpp SRC /main/ CPP /*.c CPP /main/ CPP /*/*.c) # add the name of the path variable that Defines the bmDEFINES the name of the site the # location of the NDK library. log-lib # Specifies the name of the NDK library that # CMake needs to locate. log) # add libfmod. So add_library(libfmod SHARED IMPORTED) # add libfmod IMPORTED_LOCATION ${FMOD_LIB_DIR}/ IMPORTED_LOCATION ${FMOD_LIB_DIR}/libfmod.so) Set_target_properties (libfmodL PROPERTIES IMPORTED_LOCATION ${FMOD_LIB_DIR}/ libfmodl.so Add_library (myfmod # set the name of this custom library to myfmod SHARED # set the property to "SHARED" ${my_source} # Specify one or more files, Provide relative paths to source files (C/C++ files) # Specify the libraries that need to be dynamically linked, and specify the resulting so library. Target_link_libraries (myfmod libfmod libfmodL ${log-lib})Copy the code

By the way: libfmod. So and libfomdL. So differ only in that the latter provides the logging function


4. FMOD basis

4.1 the initialization

// Load the library file, where the name depends on you
System.loadLibrary("libfmod")
System.loadLibrary("libfmodL")
System.loadLibrary("myfmod") 

// It is recommended to call the FMOD initialization method in onCreate of the main Activity
org.fmod.FMOD.init(this);
Copy the code

4.2 DSP Effector

The FMOD SDK provides a series of DSP effects that allow developers to quickly adjust audio parameters. See the file fmod_dsp_effects.h in demo for details

typedef enum
{
    FMOD_DSP_TYPE_UNKNOWN,
    FMOD_DSP_TYPE_MIXER,
    FMOD_DSP_TYPE_OSCILLATOR,
    FMOD_DSP_TYPE_LOWPASS,
    FMOD_DSP_TYPE_ITLOWPASS,
    FMOD_DSP_TYPE_HIGHPASS,
    FMOD_DSP_TYPE_ECHO,
    FMOD_DSP_TYPE_FADER,
    FMOD_DSP_TYPE_FLANGE,
    FMOD_DSP_TYPE_DISTORTION,
    FMOD_DSP_TYPE_NORMALIZE,
    FMOD_DSP_TYPE_LIMITER,
    FMOD_DSP_TYPE_PARAMEQ,
    FMOD_DSP_TYPE_PITCHSHIFT,
    FMOD_DSP_TYPE_CHORUS,
    FMOD_DSP_TYPE_VSTPLUGIN,
    FMOD_DSP_TYPE_WINAMPPLUGIN,
    FMOD_DSP_TYPE_ITECHO,
    FMOD_DSP_TYPE_COMPRESSOR,
    FMOD_DSP_TYPE_SFXREVERB,
    FMOD_DSP_TYPE_LOWPASS_SIMPLE,
    FMOD_DSP_TYPE_DELAY,
    FMOD_DSP_TYPE_TREMOLO,
    FMOD_DSP_TYPE_LADSPAPLUGIN,
    FMOD_DSP_TYPE_SEND,
    FMOD_DSP_TYPE_RETURN,
    FMOD_DSP_TYPE_HIGHPASS_SIMPLE,
    FMOD_DSP_TYPE_PAN,
    FMOD_DSP_TYPE_THREE_EQ,
    FMOD_DSP_TYPE_FFT,
    FMOD_DSP_TYPE_LOUDNESS_METER,
    FMOD_DSP_TYPE_ENVELOPEFOLLOWER,
    FMOD_DSP_TYPE_CONVOLUTIONREVERB,
    FMOD_DSP_TYPE_CHANNELMIX,
    FMOD_DSP_TYPE_TRANSCEIVER,
    FMOD_DSP_TYPE_OBJECTPAN,
    FMOD_DSP_TYPE_MULTIBAND_EQ,
    FMOD_DSP_TYPE_MAX,
    FMOD_DSP_TYPE_FORCEINT = 65536    /* Makes sure this enum is signed 32bit. */
} FMOD_DSP_TYPE;
Copy the code

The above are the supported DSP effectors. For example, when you want to perform echo and pitch processing on audio, the core code is as follows. The principle is very simple, and the DSP effector can be directly superimposed on the corresponding audio playback channel

System *system; Channel *channel; Sound *sound; / /... // Echo echo echo; system->createDSPByType(FMOD_DSP_TYPE_ECHO, &echoDsp); Echo ->setParameterFloat(FMOD_DSP_ECHO_DELAY, echoDelay); Echo ->setParameterFloat(fmod_dsp_echo feedback, echoFeedback); Echo ->setParameterFloat(FMOD_DSP_ECHO_DRYLEVEL, echoDryLevel); Echo ->setParameterFloat(FMOD_DSP_ECHO_WETLEVEL, echoWetLevel); echo ->setParameterFloat(fmod_dSP_echo wetlevel, echoWetLevel); Channel ->addDSP(0, echoDsp); channel->addDSP(0, echoDsp); // DSP *pitchDsp; system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &pitchDsp); PitchDsp ->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, Pitch); PitchDsp ->setParameterFloat(FMOD_DSP_PITCHSHIFT_FFTSIZE, fftSize); channel->addDSP(0, pitchDsp); / /... Omit the playback processCopy the code

The same can be done for other DSP effectors.

4.3 Multi-audio fusion

Multi-audio fusion is very simple, play corresponding audio files on multiple sound track channels at the same time

system->playSound(sound1, 0, false, &channel1); system->playSound(sound2, 0, false, &channel2); system->playSound(sound3, 0, false, &channel3); .Copy the code

4.4 Save audio Files after audio processing

The core code

system->setOutput(FMOD_OUTPUTTYPE_WAVWRITER); / / save the file format is WAV system - > init (32, FMOD_INIT_NORMAL | FMOD_INIT_PROFILE_ENABLE, output path {}); // Save the core code of the compositing effectCopy the code

After the audio is processed, the audio file in WAV format will be output in the specified path

4.5 Core C code written during the test for reference only

Note: do not ridicule the author to write C code is chaotic, the author is also to drive a duck to write temporarily, everyone gather together to live see….

#include <jni.h>
#include <string>
#include "aisound.h"
#include <fmod.hpp>
#include <android/log.h>
#include <unistd.h>
#include <cstring>
#include <fmod_errors.h>

#define TAG "FMOD"
#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,TAG,FORMAT,##__VA_ARGS__);
#define LOGD(FORMAT, ...) __android_log_print(ANDROID_LOG_DEBUG,TAG,FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,TAG,FORMAT,##__VA_ARGS__);

using namespace FMOD;

/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * hybrid broadcast voice effect * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /


/** * auxiliary methods */
void printFmodResult(FMOD_RESULT result) {
    LOGE("%s".FMOD_ErrorString(result))
}


/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * fmod parameter adjustment Start * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /


/** * applyXXXEffect * @param env JNI tool-assisted environment variable * @param sound_effect_bean Jobject passed by the Java layer to the JNI layer. SoundEffectBean * @Param adjustChannel Fmod::System instance * @Param adjustChannel
void applyChorusSoundEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *adjustChannel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);

    / / the harmony
    jmethodID getChorusEffect = env->GetMethodID(cls, "getChorusEffect"."()Lio/microshow/aisound/soundeffect/type/ChorusEffect;");
    jobject chorusEffectObject = env->CallObjectMethod(sound_effect_bean, getChorusEffect);


    if(chorusEffectObject ! =NULL) {
        jclass chorusEffectClass = env->GetObjectClass(chorusEffectObject);

        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(chorusEffectObject, env->GetMethodID(chorusEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat chorusDepth = env->CallFloatMethod(chorusEffectObject, env->GetMethodID(chorusEffectClass, "getChorusDepth"."()F"));
        jfloat chorusMix = env->CallFloatMethod(chorusEffectObject, env->GetMethodID(chorusEffectClass, "getChorusMix"."()F"));
        jfloat chorusRate = env->CallFloatMethod(chorusEffectObject, env->GetMethodID(chorusEffectClass, "getChorusRate"."()F"));

        DSP *chorusDsp;
        system->createDSPByType(FMOD_DSP_TYPE_CHORUS, &chorusDsp);               /* This unit produces a chorus effect on the sound. */
        chorusDsp->setParameterFloat(FMOD_DSP_CHORUS_DEPTH, chorusDepth);        /* (Type:float) - Chorus Modulation depth.0.0 to 100.0. Default = 3.0. */
        chorusDsp->setParameterFloat(FMOD_DSP_CHORUS_MIX, chorusMix);            /* (Type:float) -volume of original signal to pass to output. 0.0 to 100.0. Default = 50.0
        chorusDsp->setParameterFloat(FMOD_DSP_CHORUS_RATE, chorusRate);          /* (Type:float) - Chorus modulation rate in HZ.0.0 to 20.0.default = 0.8 Hz. */
        adjustChannel->addDSP(0, chorusDsp);

        LOGI("%s%.2f"."chorusDepth:", chorusDepth)
        LOGI("%s%.2f"."chorusMix:", chorusMix)
        LOGI("%s%.2f"."chorusRate:", chorusRate)
    }
}

void applyEchoEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getEchoEffect = env->GetMethodID(cls, "getEchoEffect"."()Lio/microshow/aisound/soundeffect/type/EchoEffect;");
    jobject echoEffectObject = env->CallObjectMethod(sound_effect_bean, getEchoEffect);
    if(echoEffectObject ! =NULL) {
        jclass echoEffectClass = env->GetObjectClass(echoEffectObject);

        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(echoEffectObject, env->GetMethodID(echoEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat echoDelay = env->CallFloatMethod(echoEffectObject, env->GetMethodID(echoEffectClass, "getEchoDelay"."()F"));
        jfloat echoFeedback = env->CallFloatMethod(echoEffectObject, env->GetMethodID(echoEffectClass, "getEchoFeedback"."()F"));
        jfloat echoDryLevel = env->CallFloatMethod(echoEffectObject, env->GetMethodID(echoEffectClass, "getEchoDryLevel"."()F"));
        jfloat echoWetLevel = env->CallFloatMethod(echoEffectObject, env->GetMethodID(echoEffectClass, "getEchoWetLevel"."()F"));


        DSP *echoDsp;
        system->createDSPByType(FMOD_DSP_TYPE_ECHO, &echoDsp);                    // Control the echo
        echoDsp->setParameterFloat(FMOD_DSP_ECHO_DELAY, echoDelay);               // Delay, in ms, 10~5000, default 500
        echoDsp->setParameterFloat(FMOD_DSP_ECHO_FEEDBACK, echoFeedback);         // echo attenuation delay, 0~ 100,100 no attenuation, 0 all attenuation, default 50
        echoDsp->setParameterFloat(FMOD_DSP_ECHO_DRYLEVEL, echoDryLevel);         // Original sound volume, in db, range from -80 to 10, default 0
        echoDsp->setParameterFloat(FMOD_DSP_ECHO_WETLEVEL, echoWetLevel);         Echo signal volume, in db, range from -80 to 10, default 0
        channel->addDSP(0, echoDsp);


        LOGI("%s%.2f"."echoDelay:", echoDelay)
        LOGI("%s%.2f"."echoFeedback:", echoFeedback)
        LOGI("%s%.2f"."echoDryLevel:", echoDryLevel)
        LOGI("%s%.2f"."echoWetLevel:", echoWetLevel)
    }
}

void applyEqEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getEqEffect = env->GetMethodID(cls, "getEqEffect"."()Lio/microshow/aisound/soundeffect/type/EqEffect;");
    jobject eqEffectObject = env->CallObjectMethod(sound_effect_bean, getEqEffect);
    if(eqEffectObject ! =NULL) {
        jclass eqEffectClass = env->GetObjectClass(eqEffectObject);


        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(eqEffectObject, env->GetMethodID(eqEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat lowGain = env->CallFloatMethod(eqEffectObject, env->GetMethodID(eqEffectClass, "getLowGain"."()F"));
        jfloat midGain = env->CallFloatMethod(eqEffectObject, env->GetMethodID(eqEffectClass, "getMidGain"."()F"));
        jfloat highGain = env->CallFloatMethod(eqEffectObject, env->GetMethodID(eqEffectClass, "getHighGain"."()F"));
        jfloat lowToMidCrossover = env->CallFloatMethod(eqEffectObject, env->GetMethodID(eqEffectClass, "getLowToMidCrossover"."()F"));
        jfloat midToHighCrossover = env->CallFloatMethod(eqEffectObject, env->GetMethodID(eqEffectClass, "getMidToHighCrossover"."()F"));
        jint crossOverSlope = env->CallIntMethod(eqEffectObject, env->GetMethodID(eqEffectClass, "getCrossOverSlope"."()I"));


        DSP *eqAdjustDsp;
        system->createDSPByType(FMOD_DSP_TYPE_THREE_EQ, &eqAdjustDsp);

        // Set the following two dividers to divide the audio into three segments: 0~400 400~4000 4000~
        eqAdjustDsp->setParameterFloat(FMOD_DSP_THREE_EQ_LOWCROSSOVER, lowToMidCrossover);// Low crossover frequency, low/medium crossover point meaning, range of 10~22000, default 400. The more low frequencies are retained, the deeper the sound.
        eqAdjustDsp->setParameterFloat(FMOD_DSP_THREE_EQ_HIGHCROSSOVER, midToHighCrossover);// Medium/high crossover frequency, medium/high frequency point meaning, range of 10~22000, default 4000. The more high frequencies are retained, the more penetrating the sound is. Set to 10KHZ reverb to send sound soaring into the air, but lower is more natural.
        eqAdjustDsp->setParameterInt(FMOD_DSP_THREE_EQ_CROSSOVERSLOPE, crossOverSlope);// There are three options: 1= 12dB /Octave,2=24db/Octave,3=48db/Octave. The default value is 1.

        // Set the db values of the three audio segments
        eqAdjustDsp->setParameterFloat(FMOD_DSP_THREE_EQ_LOWGAIN, lowGain);// Low frequency DB gain, range -80 to 10, default value 0
        eqAdjustDsp->setParameterFloat(FMOD_DSP_THREE_EQ_MIDGAIN, midGain);// Medium frequency DB gain, range -80 to 10, default value 0
        eqAdjustDsp->setParameterFloat(FMOD_DSP_THREE_EQ_HIGHGAIN, highGain);// High frequency DB gain, range -80 to 10, default value 0

        channel->addDSP(0, eqAdjustDsp);


        LOGI("%s%.2f"."lowGain:", lowGain)
        LOGI("%s%.2f"."midGain:", midGain)
        LOGI("%s%.2f"."highGain:", highGain)
        LOGI("%s%.2f"."lowToMidCrossover:", lowToMidCrossover)
        LOGI("%s%.2f"."midToHighCrossover:", midToHighCrossover)
        LOGI("%s%d"."crossOverSlope:", crossOverSlope)
    }
}

void applyFlangeEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getFlangeEffect = env->GetMethodID(cls, "getFlangeEffect"."()Lio/microshow/aisound/soundeffect/type/FlangeEffect;");
    jobject flangeEffectObject = env->CallObjectMethod(sound_effect_bean, getFlangeEffect);
    if(flangeEffectObject ! =NULL) {
        jclass flangeEffectClass = env->GetObjectClass(flangeEffectObject);

        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(flangeEffectObject, env->GetMethodID(flangeEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat flangeMix = env->CallFloatMethod(flangeEffectObject, env->GetMethodID(flangeEffectClass, "getFlangeMix"."()F"));
        jfloat flangeDepth = env->CallFloatMethod(flangeEffectObject, env->GetMethodID(flangeEffectClass, "getFlangeDepth"."()F"));
        jfloat flangeRate = env->CallFloatMethod(flangeEffectObject, env->GetMethodID(flangeEffectClass, "getFlangeRate"."()F"));


        DSP *flangeDsp;
        system->createDSPByType(FMOD_DSP_TYPE_FLANGE, &flangeDsp);
        flangeDsp->setParameterFloat(FMOD_DSP_FLANGE_MIX, flangeMix);                 // The value ranges from 0 to 100. The default value is 50
        flangeDsp->setParameterFloat(FMOD_DSP_FLANGE_DEPTH, flangeDepth);             // The value ranges from 0.01 to 1.0. The default value is 1.0
        flangeDsp->setParameterFloat(FMOD_DSP_FLANGE_RATE, flangeRate);               // The range is 0.0 to 20, in Hz. The default value is 0.1
        channel->addDSP(0, flangeDsp);

        LOGI("%s%.2f"."flangeMix:", flangeMix)
        LOGI("%s%.2f"."flangeDepth:", flangeDepth)
        LOGI("%s%.2f"."flangeRate:", flangeRate)
    }
}

void applyFrequencyEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channle) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getFrequencyEffect = env->GetMethodID(cls, "getFrequencyEffect"."()Lio/microshow/aisound/soundeffect/type/FrequencyEffect;");
    jobject frequencyEffectObject = env->CallObjectMethod(sound_effect_bean, getFrequencyEffect);
    if(frequencyEffectObject ! =NULL) {
        jclass frequencyEffectClass = env->GetObjectClass(frequencyEffectObject);


        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(frequencyEffectObject, env->GetMethodID(frequencyEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat frequency = env->CallFloatMethod(frequencyEffectObject, env->GetMethodID(frequencyEffectClass, "getFrequency"."()F"));


        // Speak as fast as possible
        float originalFrequency;
        channle->getFrequency(&originalFrequency);
        channle->setFrequency(originalFrequency * frequency);

        LOGI("%s%.2f"."frequencyTimes:", frequency)
    }

}

void applyPitchEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getPitchEffect = env->GetMethodID(cls, "getPitchEffect"."()Lio/microshow/aisound/soundeffect/type/PitchEffect;");
    jobject pitchEffectObject = env->CallObjectMethod(sound_effect_bean, getPitchEffect);
    if(pitchEffectObject ! =NULL) {
        jclass pitchEffectClass = env->GetObjectClass(pitchEffectObject);

        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(pitchEffectObject, env->GetMethodID(pitchEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat pitch = env->CallFloatMethod(pitchEffectObject, env->GetMethodID(pitchEffectClass, "getPitch"."()F"));
        jfloat fftSize = env->CallFloatMethod(pitchEffectObject, env->GetMethodID(pitchEffectClass, "getFftSize"."()F"));


        DSP *pitchDsp;
        system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &pitchDsp);// Change Pitch
        pitchDsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, pitch);//1.0 is the standard value. Each 0.5 change in the upper and lower values represents an 8 degree, ranging from 0.5 to 2.0
        pitchDsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_FFTSIZE, fftSize);
        channel->addDSP(0, pitchDsp);

        LOGI("%s%.2f"."pitch:", pitch)
        LOGI("%s%.2f"."fftSize:", fftSize)
    }
}

void applyTremoloEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getTremoloEffect = env->GetMethodID(cls, "getTremoloEffect"."()Lio/microshow/aisound/soundeffect/type/TremoloEffect;");
    jobject tremoloEffectObject = env->CallObjectMethod(sound_effect_bean, getTremoloEffect);
    if(tremoloEffectObject ! =NULL) {
        jclass tremoloEffectClass = env->GetObjectClass(tremoloEffectObject);


        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat tremoloDepth = env->CallFloatMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "getTremoloDepth"."()F"));
        jfloat tremoloDuty = env->CallFloatMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "getTremoloDuty"."()F"));
        jfloat tremoloFrequency = env->CallFloatMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "getTremoloFrequency"."()F"));
        jfloat tremoloPhase = env->CallFloatMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "getTremoloPhase"."()F"));
        jfloat tremoloShape = env->CallFloatMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "getTremoloShape"."()F"));
        jfloat tremoloSkew = env->CallFloatMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "getTremoloSkew"."()F"));
        jfloat tremoloSpread = env->CallFloatMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "getTremoloSpread"."()F"));
        jfloat tremoloSquare = env->CallFloatMethod(tremoloEffectObject, env->GetMethodID(tremoloEffectClass, "getTremoloSquare"."()F"));


        DSP *tremoloDsp;
        system->createDSPByType(FMOD_DSP_TYPE_TREMOLO, &tremoloDsp);
        //TREMOLO DEPTH(TREMOLO DEPTH) is used to adjust the pitch of the TREMOLO
        tremoloDsp->setParameterFloat(FMOD_DSP_TREMOLO_DEPTH, tremoloDepth);            /* (Type:float) - Tremolo depth. 0 to 1. Default = 1. */
        tremoloDsp->setParameterFloat(FMOD_DSP_TREMOLO_DUTY, tremoloDuty);              /* (Type:float) -lfo on-time. 0 to 1.default = 0.5. */
        tremoloDsp->setParameterFloat(FMOD_DSP_TREMOLO_FREQUENCY, tremoloFrequency);    /* (Type:float) -lFO frequency in HZ.0.1 to 20.default = 5. */
        tremoloDsp->setParameterFloat(FMOD_DSP_TREMOLO_PHASE, tremoloPhase);            /* (Type:float) - Instantaneous LFO phase. 0 to 1. Default = 0. */
        tremoloDsp->setParameterFloat(FMOD_DSP_TREMOLO_SHAPE, tremoloShape);            /* (Type:float) - LFO shape morph between triangle and sine. 0 to 1. Default = 0. */
        tremoloDsp->setParameterFloat(FMOD_DSP_TREMOLO_SKEW, tremoloSkew);              /* (Type:float) - Time-skewing of LFO cycle. -1 to 1. Default = 0. */
        tremoloDsp->setParameterFloat(FMOD_DSP_TREMOLO_SPREAD, tremoloSpread);          /* (Type:float) - Rotation / auto-pan effect. -1 to 1. Default = 0. */
        tremoloDsp->setParameterFloat(FMOD_DSP_TREMOLO_SQUARE, tremoloSquare);          /* (Type:float) - Flatness of the LFO shape. 0 to 1. Default = 0. */
        channel->addDSP(0, tremoloDsp);


        LOGI("%s%.2f"."tremoloDepth:", tremoloDepth);
        LOGI("%s%.2f"."tremoloDuty:", tremoloDuty);
        LOGI("%s%.2f"."tremoloFrequency:", tremoloFrequency);
        LOGI("%s%.2f"."tremoloPhase:", tremoloPhase);
        LOGI("%s%.2f"."tremoloShape:", tremoloShape);
        LOGI("%s%.2f"."tremoloSkew:", tremoloSkew);
        LOGI("%s%.2f"."tremoloSpread:", tremoloSpread);
        LOGI("%s%.2f"."tremoloSquare:", tremoloSquare); }}void applyNormalizeEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getNormalizeEffect = env->GetMethodID(cls, "getNormalizeEffect"."()Lio/microshow/aisound/soundeffect/type/NormalizeEffect;");
    jobject normalizeEffectObject = env->CallObjectMethod(sound_effect_bean, getNormalizeEffect);
    if(normalizeEffectObject ! =NULL) {
        jclass normalizeEffectClass = env->GetObjectClass(normalizeEffectObject);

        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(normalizeEffectObject, env->GetMethodID(normalizeEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;

        // Effect parameters
        jfloat fadeTime = env->CallFloatMethod(normalizeEffectObject, env->GetMethodID(normalizeEffectClass, "getFadeTime"."()F"));
        jfloat threshold = env->CallFloatMethod(normalizeEffectObject, env->GetMethodID(normalizeEffectClass, "getThreshold"."()F"));
        jfloat maxAmp = env->CallFloatMethod(normalizeEffectObject, env->GetMethodID(normalizeEffectClass, "getMaxAmp"."()F"));


        DSP *normalizeDsp;
        system->createDSPByType(FMOD_DSP_TYPE_NORMALIZE, &normalizeDsp);    // Amplify the sound
        normalizeDsp->setParameterFloat(FMOD_DSP_NORMALIZE_FADETIME, 5000.0 f);  /* (Type:float) - Time to ramp the silence to full in Ms. 0.0 to 20000.0. Default = 5000.0. */
        normalizeDsp->setParameterFloat(FMOD_DSP_NORMALIZE_THRESHHOLD,
                                        0.1 f);    /* (Type:float) -lower volume range threshold to ignore.0.0 to 1.0.default = 0.1. Raise higher to stop amplification of very quiet signals. */
        normalizeDsp->setParameterFloat(FMOD_DSP_NORMALIZE_MAXAMP,
                                        20.0 f);       /* (Type:float) - Maximum amplification allowed. 1.0 to 100000.0. Default = 20.0.1.0 = no amplifaction, higher values allow more boost. */

        channel->addDSP(0, normalizeDsp);
        LOGI("%s%.2f"."fadeTime:", fadeTime);
        LOGI("%s%.2f"."threshold:", threshold);
        LOGI("%s%.2f"."maxAmp:", maxAmp); }}void applySfxReverbEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getSfxReverbEffect = env->GetMethodID(cls, "getSfxReverbEffect"."()Lio/microshow/aisound/soundeffect/type/SfxReverbEffect;");
    jobject sfxReverbEffectObject = env->CallObjectMethod(sound_effect_bean, getSfxReverbEffect);
    if(sfxReverbEffectObject ! =NULL) {
        jclass sfxReverbEffectClass = env->GetObjectClass(sfxReverbEffectObject);

        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat decayTime = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getDecayTime"."()F"));
        jfloat earlyDelay = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getEarlyDelay"."()F"));
        jfloat lateDelay = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getLateDelay"."()F"));
        jfloat hfReference = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getHfReference"."()F"));
        jfloat hfDecayRatio = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getHfDecayRatio"."()F"));
        jfloat diffusion = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getDiffusion"."()F"));
        jfloat density = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getDensity"."()F"));
        jfloat lowShelfFrequency = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getLowShelfFrequency"."()F"));
        jfloat lowShelfGain = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getLowShelfGain"."()F"));
        jfloat highCut = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getHighCut"."()F"));
        jfloat earlyLateMix = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getEarlyLateMix"."()F"));
        jfloat wetLevel = env->CallFloatMethod(sfxReverbEffectObject, env->GetMethodID(sfxReverbEffectClass, "getWetLevel"."()F"));


        DSP *sfxReverbDsp;
        system->createDSPByType(FMOD_DSP_TYPE_SFXREVERB, &sfxReverbDsp);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_DECAYTIME, decayTime);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_EARLYDELAY, earlyDelay);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_LATEDELAY, lateDelay);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_HFREFERENCE, hfReference);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_HFDECAYRATIO, hfDecayRatio);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_DIFFUSION, diffusion);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_DENSITY, density);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_LOWSHELFFREQUENCY, lowShelfFrequency);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_LOWSHELFGAIN, lowShelfGain);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_HIGHCUT, highCut);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_EARLYLATEMIX, earlyLateMix);
        sfxReverbDsp->setParameterFloat(FMOD_DSP_SFXREVERB_WETLEVEL, wetLevel);
        channel->addDSP(0, sfxReverbDsp);


        LOGI("%s%.2f"."decayTime:", decayTime);
        LOGI("%s%.2f"."earlyDelay:", earlyDelay);
        LOGI("%s%.2f"."lateDelay:", lateDelay);
        LOGI("%s%.2f"."hfReference:", hfReference);
        LOGI("%s%.2f"."hfDecayRatio:", hfDecayRatio);
        LOGI("%s%.2f"."Coursing together.", diffusion);
        LOGI("%s%.2f"."density:", density);
        LOGI("%s%.2f"."lowShelfFrequency:", lowShelfFrequency);
        LOGI("%s%.2f"."lowShelfGain:", lowShelfGain);
        LOGI("%s%.2f"."highCut:", highCut);
        LOGI("%s%.2f"."earlyLateMix:", earlyLateMix);
        LOGI("%s%.2f"."wetLevel:", wetLevel); }}void applyDistortionEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getDistortionEffect = env->GetMethodID(cls, "getDistortionEffect"."()Lio/microshow/aisound/soundeffect/type/DistortionEffect;");
    jobject distortionEffectObject = env->CallObjectMethod(sound_effect_bean, getDistortionEffect);
    if(distortionEffectObject ! =NULL) {
        jclass distortionEffectClass = env->GetObjectClass(distortionEffectObject);

        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(distortionEffectObject, env->GetMethodID(distortionEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jfloat distortionLevel = env->CallFloatMethod(distortionEffectObject, env->GetMethodID(distortionEffectClass, "getDistortionLevel"."()F"));

        DSP *distortionDsp;
        system->createDSPByType(FMOD_DSP_TYPE_DISTORTION, &distortionDsp);
        distortionDsp->setParameterFloat(FMOD_DSP_DISTORTION_LEVEL, distortionLevel);
        channel->addDSP(0, distortionDsp);

        LOGI("%s%.2f"."distortionLevel:", distortionLevel); }}void applyOscillatorEffectEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {
    jclass cls = env->GetObjectClass(sound_effect_bean);
    jmethodID getOscillatorEffect = env->GetMethodID(cls, "getOscillatorEffect"."()Lio/microshow/aisound/soundeffect/type/OscillatorEffect;");
    jobject oscillatorEffectObject = env->CallObjectMethod(sound_effect_bean, getOscillatorEffect);
    if(oscillatorEffectObject ! =NULL) {
        jclass oscillatorEffectClass = env->GetObjectClass(oscillatorEffectObject);

        // Whether to enable the sound change effect
        jboolean isEffectActive = env->CallBooleanMethod(oscillatorEffectObject, env->GetMethodID(oscillatorEffectClass, "isActive"."()Z"));
        if(! isEffectActive)return;


        // Effect parameters
        jint oscillatorType = env->CallIntMethod(oscillatorEffectObject, env->GetMethodID(oscillatorEffectClass, "getOscillatorType"."()I"));
        jfloat oscillatorRate = env->CallFloatMethod(oscillatorEffectObject, env->GetMethodID(oscillatorEffectClass, "getOscillatorRate"."()F"));

        DSP *oscillatorDsp;
        system->createDSPByType(FMOD_DSP_TYPE_OSCILLATOR, &oscillatorDsp);
        oscillatorDsp->setParameterInt(FMOD_DSP_OSCILLATOR_TYPE, oscillatorType);
        oscillatorDsp->setParameterFloat(FMOD_DSP_OSCILLATOR_RATE, oscillatorRate);
        channel->addDSP(0, oscillatorDsp);

        LOGI("%s%d"."oscillatorType:", oscillatorType);
        LOGI("%s%.2f"."oscillatorRate:", oscillatorRate); }}void applyAllSoundEffect(JNIEnv *env, jobject sound_effect_bean, System *system, Channel *channel) {

    applyChorusSoundEffect(env, sound_effect_bean, system, channel);
    applyEchoEffect(env, sound_effect_bean, system, channel);
    applyEqEffect(env, sound_effect_bean, system, channel);
    applyFlangeEffect(env, sound_effect_bean, system, channel);
    applyFrequencyEffect(env, sound_effect_bean, system, channel);
    applyPitchEffect(env, sound_effect_bean, system, channel);
    applyTremoloEffect(env, sound_effect_bean, system, channel);
    applyNormalizeEffect(env, sound_effect_bean, system, channel);
    applySfxReverbEffect(env, sound_effect_bean, system, channel);
    applyDistortionEffect(env, sound_effect_bean, system, channel);
    applyOscillatorEffectEffect(env, sound_effect_bean, system, channel);
}

/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * fmod parameters to adjust the End * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /






Sound *mainSound, *subSound;
Channel *mainChannel, *subChannel;
bool loopMode;

jobject soundEffect;

extern "C"
JNIEXPORT void JNICALL
Java_io_microshow_aisound_AiSound_playMixSound
        (JNIEnv *env, jclass clazz, jstring main_audio, jstring sub_audio, jfloat main_audio_volume, jfloat sub_audio_volume, jobject sound_effect, jboolean isLoop, jobject callBack) {

    / / declare
    System *system;
    bool isPlaying = true;
    bool isPause = false;

    // The system object is initialized
    System_Create(&system);
    system->init(32, FMOD_INIT_NORMAL, NULL);


    //C/C++ convert the String from Java to use it
    const char *cstr_main_audio = env->GetStringUTFChars(main_audio, NULL);
    const char *cstr_sub_audio = env->GetStringUTFChars(sub_audio, NULL);


    // The current playback position, in ms
    unsigned int playPosition;

    // Actual playback duration, in ms
    jlong actualPlayTime = 0l;


    // Whether to loop
    loopMode = isLoop;

    // the C++ layer invokes Java layer methods, i.e., upper-level callbacks
    jclass cls = env->GetObjectClass(callBack);
    jmethodID onStartMethod = env->GetMethodID(cls, "onStart"."()V");
    jmethodID onTimeMethod = env->GetMethodID(cls, "onTime"."(JJ)V");
    jmethodID onCompleteMethod = env->GetMethodID(cls, "onComplete"."()V");
    jmethodID onErrorMethod = env->GetMethodID(cls, "onError"."(Ljava/lang/String;) V");


    try {
        // Main audio processing
        system->createSound(cstr_main_audio, loopMode ? FMOD_LOOP_NORMAL : FMOD_DEFAULT, NULL, &mainSound);
        system->playSound(mainSound, 0.false, &mainChannel);
        mainChannel->setVolume(main_audio_volume);


        // Add a sound change effect
        soundEffect = sound_effect;// Record the first sound change parameters
        applyAllSoundEffect(env, sound_effect, system, mainChannel);


        // Subaudio processing. If an empty string is passed in, no subaudio is added
        if (env->GetStringLength(sub_audio) ! =0) {
            //LOGE("%s", "background sound ")
            system->createSound(cstr_sub_audio, FMOD_LOOP_NORMAL, NULL, &subSound);//subSound is background music, let it play until MainSound is finished playing
            system->playSound(subSound, 0.false, &subChannel);
            subChannel->setVolume(sub_audio_volume);
        }


        // Notify the upper layer before playing
        env->CallVoidMethod(callBack, onStartMethod);

    } catch(...). { jstring data = env->NewStringUTF("Abnormal mixing playback");
        env->CallVoidMethod(callBack, onErrorMethod, data);
        goto end;
    }


    // check the current status every 100ms
    while (isPlaying) {
        usleep(1000 * 100);// Note that the parameter is in microseconds, 1s=10^6 microseconds
        mainChannel->isPlaying(&isPlaying);


        // Notify the upper layer of the current playback progress, unit ms,
        mainChannel->getPaused(&isPause);
        if(! isPause) {// Get the relative playback time
            mainChannel->getPosition(&playPosition, FMOD_TIMEUNIT_MS);
            jlong relativePlayTime = playPosition;// Note that the playback progress refers to the current progress of the original track, not the actual playback time progress

            // Manually calculate the actual playing time, in ms
            actualPlayTime = actualPlayTime + 100;

            // The notification interval is 1s, that is, the number of whole seconds to send the notification
            if (actualPlayTime % 1000= =0) {
                env->CallVoidMethod(callBack, onTimeMethod, relativePlayTime, actualPlayTime); }}// Determine whether the listening layer changes the loop playback Settings during playback
        if(loopMode ! = isLoop) { isLoop =static_cast<jboolean>(loopMode);
            mainChannel->setMode(loopMode ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); }}// Call the callback function after the playback
    env->CallVoidMethod(callBack, onCompleteMethod);

    goto end;
    end:


    // Release resource processing
    env->ReleaseStringUTFChars(main_audio, cstr_main_audio);
    env->ReleaseStringUTFChars(sub_audio, cstr_sub_audio);
    mainSound->release(a); subSound->release(a); system->close(a); system->release(a); }extern "C"
JNIEXPORT jint JNICALL
Java_io_microshow_aisound_AiSound_saveMixSound
        (JNIEnv *env, jclass clazz, jstring main_audio, jstring sub_audio, jstring output_audio, jfloat main_audio_volume, jfloat sub_audio_volume, jobject sound_effect) {

    //GetStringUTFChars converts a JString pointer (pointing to a sequence of Unicode characters within the JVM) to a UTF-8 C string
    const char *cstr_main_audio = env->GetStringUTFChars(main_audio, NULL);            // Main audio path
    const char *cstr_sub_audio = env->GetStringUTFChars(sub_audio, NULL);              // Subaudio path
    const char *cstr_output_audio = env->GetStringUTFChars(output_audio, NULL);        // The output audio path specified by the upper layer


    / / declare
    System *system;
    Sound *mainSound, *subSound;
    Channel *mainChannel, *subChannel;
    bool isMainSoundPlaying = true;
    jint result = 1;


    / / initialization
    System_Create(&system);
    // Set the sampling rate to 48000, mono. Note that the SampleRate sampling rate parameter greatly affects audio quality and is recommended to be the same as that used for recording
    system->setSoftwareFormat(48000, FMOD_SPEAKERMODE_MONO, 0);

    char cDest[200];
    strcpy(cDest, cstr_output_audio);
    system->setOutput(FMOD_OUTPUTTYPE_WAVWRITER);                        // Save the file in WAV format
    system->init(32, FMOD_INIT_NORMAL | FMOD_INIT_PROFILE_ENABLE, cDest);// Save the core code of the compositing effect

    try {
        // Main audio processing
        system->createSound(cstr_main_audio, FMOD_DEFAULT, NULL, &mainSound);
        system->playSound(mainSound, 0.false, &mainChannel);
        mainChannel->setVolume(main_audio_volume);

        // Add a sound change effect
        applyAllSoundEffect(env, sound_effect, system, mainChannel);


        // Subaudio processing
        if (env->GetStringLength(sub_audio) ! =0) {
            LOGI("%s"."Processing secondary audio, background sound.")
            system->createSound(cstr_sub_audio, FMOD_LOOP_NORMAL, NULL, &subSound);//subSound is background music, let it play until MainSound is finished playing
            system->playSound(subSound, 0.false, &subChannel);
            subChannel->setVolume(sub_audio_volume); }}catch(...). {LOGE("%s"."Abnormal sound change effect saved")
        result = 0;
    }


    try {
        system->update(a);while (isMainSoundPlaying) {
            mainChannel->isPlaying(&isMainSoundPlaying);
            usleep(1000);
        }
        // Release resource processing
        env->ReleaseStringUTFChars(main_audio, cstr_main_audio);
        env->ReleaseStringUTFChars(sub_audio, cstr_sub_audio);
        env->ReleaseStringUTFChars(output_audio, cstr_output_audio);

        mainSound->release(a); subSound->release(a); system->close(a); system->release(a); }catch(...). {LOGE("%s"."Abnormal sound change effect saved")
        result = 0;
    }

    return result;
}



extern "C"
JNIEXPORT void JNICALL
Java_io_microshow_aisound_AiSound_setMixSoundMainVolume(JNIEnv *env, jclass clazz, jfloat volume) {
    LOGI("%s"."--> AiSound_setMixSoundMainVolume");
    mainChannel->setVolume(volume);
}


extern "C"
JNIEXPORT void JNICALL
Java_io_microshow_aisound_AiSound_setMixSoundSubVolume(JNIEnv *env, jclass clazz, jfloat volume) {
    LOGI("%s"."--> AiSound_setMixSoundSubVolume");
    subChannel->setVolume(volume);
}


extern "C"
JNIEXPORT void JNICALL
Java_io_microshow_aisound_AiSound_pauseMixSound(JNIEnv *env, jclass clazz) {
    LOGI("%s"."--> AiSound_pauseMixSound");
    mainChannel->setPaused(true);
    subChannel->setPaused(true);

}


extern "C"
JNIEXPORT void JNICALL
Java_io_microshow_aisound_AiSound_resumeMixSound(JNIEnv *env, jclass clazz) {
    LOGI("%s"."--> AiSound_resumeMixSound");
    mainChannel->setPaused(false);
    subChannel->setPaused(false);
}


extern "C"
JNIEXPORT jboolean JNICALL
Java_io_microshow_aisound_AiSound_isMixPlay(JNIEnv *env, jclass clazz) {
    bool isPlaying = true;
    return! mainChannel->isPlaying(&isPlaying);
}


extern "C"
JNIEXPORT void JNICALL
Java_io_microshow_aisound_AiSound_stopMixSound(JNIEnv *env, jclass clazz) {
    LOGI("%s"."--> AiSound_stopMixSound");
    mainChannel->stop(a); }extern "C"
JNIEXPORT void JNICALL
Java_io_microshow_aisound_AiSound_changeLoopMode(JNIEnv *env, jclass clazz, jboolean is_loop) {
    loopMode = is_loop;
}




extern "C"
JNIEXPORT void JNICALL
Java_io_microshow_aisound_AiSound_changeSoundEffectWhilePlaying(JNIEnv *env, jclass clazz, jobject sound_effect) {
    soundEffect = sound_effect;
}
Copy the code