安卓 : How to change Playback Rate of music using OpenSL ES

标签 android audio multimedia soundpool audiotrack

我正在开发一个音乐播放器,我需要在不改变音高的情况下改变节奏(音乐的播放速度)。

我无法找到任何 native android 类来执行此操作。我尝试了 SoundPool,但它不适用于大型音乐文件,而且它似乎也不适用于许多设备。我也尝试了 AudioTrack,但还是没有运气。

现在我正在尝试使用 OpenSL ES 处理音乐的 android NDK 音频示例。现在我只想在此示例中添加设置播放速率功能。

谁能告诉我如何在其中添加更改播放速率功能?

最佳答案

我已经解决了我的问题。如果有人需要,这是我完整的 OpenSL ES 原生代码:

#include <jni.h>

#include<android/log.h>
// LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog 넣어주세요
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "OSLESMediaPlayer", __VA_ARGS__) 
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , "OSLESMediaPlayer", __VA_ARGS__) 
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO   , "OSLESMediaPlayer", __VA_ARGS__) 
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN   , "OSLESMediaPlayer", __VA_ARGS__) 
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , "OSLESMediaPlayer", __VA_ARGS__) 

// for native audio
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

#include <assert.h>
#include <sys/types.h>

// engine interfaces
static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine;

// URI player interfaces
static SLObjectItf uriPlayerObject = NULL;
static SLPlayItf uriPlayerPlay;
static SLSeekItf uriPlayerSeek;
static SLPlaybackRateItf uriPlaybackRate;

// output mix interfaces
static SLObjectItf outputMixObject = NULL;

// playback rate (default 1x:1000)
static SLpermille playbackMinRate = 500;
static SLpermille playbackMaxRate = 2000;
static SLpermille playbackRateStepSize;

//Pitch
static SLPitchItf uriPlaybackPitch;
static SLpermille playbackMinPitch = 500;
static SLpermille playbackMaxPitch = 2000;

// create the engine and output mix objects
JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_createEngine(
        JNIEnv* env, jclass clazz) {
    SLresult result;

    // create engine
    LOGD("create engine");
    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    assert(SL_RESULT_SUCCESS == result);

    // realize the engine
    LOGD("realize the engine");
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    assert(SL_RESULT_SUCCESS == result);

    // get the engine interface, which is needed in order to create other objects
    LOGD("get the engine interface");
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE,
            &engineEngine);
    assert(SL_RESULT_SUCCESS == result);

    // create output mix, with environmental reverb specified as a non-required interface
    LOGD("create output mix");
    const SLInterfaceID ids[1] = {SL_IID_PLAYBACKRATE};
    const SLboolean req[1] = {SL_BOOLEAN_FALSE};
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1,
            ids, req);
    assert(SL_RESULT_SUCCESS == result);

    // realize the output mix
    LOGD("realize the output mix");
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    assert(SL_RESULT_SUCCESS == result);

}

JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_releaseEngine(
        JNIEnv* env, jclass clazz) {
    // destroy URI audio player object, and invalidate all associated interfaces
    if (uriPlayerObject != NULL) {
        (*uriPlayerObject)->Destroy(uriPlayerObject);
        uriPlayerObject = NULL;
        uriPlayerPlay = NULL;
        uriPlayerSeek = NULL;
    }

    // destroy output mix object, and invalidate all associated interfaces
    if (outputMixObject != NULL) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
    }

    // destroy engine object, and invalidate all associated interfaces
    if (engineObject != NULL) {
        (*engineObject)->Destroy(engineObject);
        engineObject = NULL;
        engineEngine = NULL;
    }

}

/*
 void OnCompletion(JNIEnv* env, jclass clazz)
 {
 jclass cls = env->GetObjectClass(thiz);
 if (cls != NULL)
 {
 jmethodID mid = env->GetMethodID(cls, "OnCompletion", "()V");
 if (mid != NULL)
 {
 env->CallVoidMethod(thiz, mid, 1234);
 }
 }
 }*/

void playStatusCallback(SLPlayItf play, void* context, SLuint32 event) {
    //LOGD("playStatusCallback");
}

// create URI audio player
JNIEXPORT jboolean Java_com_swssm_waveloop_audio_OSLESMediaPlayer_createAudioPlayer(
        JNIEnv* env, jclass clazz, jstring uri) {
    SLresult result;

    // convert Java string to UTF-8
    const jbyte *utf8 = (*env)->GetStringUTFChars(env, uri, NULL);
    assert(NULL != utf8);

    // configure audio source
    // (requires the INTERNET permission depending on the uri parameter)
    SLDataLocator_URI loc_uri = { SL_DATALOCATOR_URI, (SLchar *) utf8 };
    SLDataFormat_MIME format_mime = { SL_DATAFORMAT_MIME, NULL,
            SL_CONTAINERTYPE_UNSPECIFIED };
    SLDataSource audioSrc = { &loc_uri, &format_mime };

    // configure audio sink
    SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX,
            outputMixObject };
    SLDataSink audioSnk = { &loc_outmix, NULL };

    // create audio player
    const SLInterfaceID ids[2] = { SL_IID_SEEK, SL_IID_PLAYBACKRATE };
    const SLboolean req[2] = { SL_BOOLEAN_FALSE, SL_BOOLEAN_TRUE };
    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &uriPlayerObject,
            &audioSrc, &audioSnk, 2, ids, req);
    // note that an invalid URI is not detected here, but during prepare/prefetch on Android,
    // or possibly during Realize on other platforms
    assert(SL_RESULT_SUCCESS == result);

    // release the Java string and UTF-8
    (*env)->ReleaseStringUTFChars(env, uri, utf8);

    // realize the player
    result = (*uriPlayerObject)->Realize(uriPlayerObject, SL_BOOLEAN_FALSE);
    // this will always succeed on Android, but we check result for portability to other platforms
    if (SL_RESULT_SUCCESS != result) {
        (*uriPlayerObject)->Destroy(uriPlayerObject);
        uriPlayerObject = NULL;
        return JNI_FALSE;
    }

    // get the play interface
    result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_PLAY,
            &uriPlayerPlay);
    assert(SL_RESULT_SUCCESS == result);

    // get the seek interface
    result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_SEEK,
            &uriPlayerSeek);
    assert(SL_RESULT_SUCCESS == result);

    // get playback rate interface
    result = (*uriPlayerObject)->GetInterface(uriPlayerObject,
            SL_IID_PLAYBACKRATE, &uriPlaybackRate);
    assert(SL_RESULT_SUCCESS == result);

    /*  // get playback pitch interface
     result = (*uriPlayerObject)->GetInterface(uriPlayerObject, SL_IID_PITCH, &uriPlaybackPitch);
     assert(SL_RESULT_SUCCESS == result);*/

    // register callback function
    result = (*uriPlayerPlay)->RegisterCallback(uriPlayerPlay,
            playStatusCallback, 0);
    assert(SL_RESULT_SUCCESS == result);
    result = (*uriPlayerPlay)->SetCallbackEventsMask(uriPlayerPlay,
            SL_PLAYEVENT_HEADATEND); // head at end
    assert(SL_RESULT_SUCCESS == result);

    SLmillisecond msec;
    result = (*uriPlayerPlay)->GetDuration(uriPlayerPlay, &msec);
    assert(SL_RESULT_SUCCESS == result);

    // no loop
    result = (*uriPlayerSeek)->SetLoop(uriPlayerSeek, SL_BOOLEAN_TRUE, 0, msec);
    assert(SL_RESULT_SUCCESS == result);


    SLuint32 capa;
        result = (*uriPlaybackRate)->GetRateRange(uriPlaybackRate, 0,
                &playbackMinRate, &playbackMaxRate, &playbackRateStepSize, &capa);
        assert(SL_RESULT_SUCCESS == result);

        result = (*uriPlaybackRate)->SetPropertyConstraints(uriPlaybackRate,
                        SL_RATEPROP_PITCHCORAUDIO);

                    if (SL_RESULT_PARAMETER_INVALID == result) {
                        LOGD("Parameter Invalid");
                    }
                    if (SL_RESULT_FEATURE_UNSUPPORTED == result) {
                            LOGD("Feature Unsupported");
                        }
                    if (SL_RESULT_SUCCESS == result) {
                        assert(SL_RESULT_SUCCESS == result);
                            LOGD("Success");
                        }
    /*
     result = (*uriPlaybackPitch)->GetPitchCapabilities(uriPlaybackPitch, &playbackMinPitch, &playbackMaxPitch);
     assert(SL_RESULT_SUCCESS == result);*/

    /*
     SLpermille minRate, maxRate, stepSize, rate = 1000;
     SLuint32 capa;
     (*uriPlaybackRate)->GetRateRange(uriPlaybackRate, 0, &minRate, &maxRate, &stepSize, &capa);

     (*uriPlaybackRate)->SetRate(uriPlaybackRate, minRate);
     */
    return JNI_TRUE;
}

JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_releaseAudioPlayer(
        JNIEnv* env, jclass clazz) {
    // destroy URI audio player object, and invalidate all associated interfaces
    if (uriPlayerObject != NULL) {
        (*uriPlayerObject)->Destroy(uriPlayerObject);
        uriPlayerObject = NULL;
        uriPlayerPlay = NULL;
        uriPlayerSeek = NULL;
        uriPlaybackRate = NULL;
    }

}

void setPlayState(SLuint32 state) {
    SLresult result;

    // make sure the URI audio player was created
    if (NULL != uriPlayerPlay) {

        // set the player's state
        result = (*uriPlayerPlay)->SetPlayState(uriPlayerPlay, state);
        assert(SL_RESULT_SUCCESS == result);
    }

}

SLuint32 getPlayState() {
    SLresult result;

    // make sure the URI audio player was created
    if (NULL != uriPlayerPlay) {

        SLuint32 state;
        result = (*uriPlayerPlay)->GetPlayState(uriPlayerPlay, &state);
        assert(SL_RESULT_SUCCESS == result);

        return state;
    }

    return 0;

}

// play
JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_play(JNIEnv* env,
        jclass clazz) {
    setPlayState(SL_PLAYSTATE_PLAYING);
}

// stop
JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_stop(JNIEnv* env,
        jclass clazz) {
    setPlayState(SL_PLAYSTATE_STOPPED);
}

// pause
JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_pause(JNIEnv* env,
        jclass clazz) {
    setPlayState(SL_PLAYSTATE_PAUSED);
}

// pause
JNIEXPORT jboolean Java_com_swssm_waveloop_audio_OSLESMediaPlayer_isPlaying(
        JNIEnv* env, jclass clazz) {
    return (getPlayState() == SL_PLAYSTATE_PLAYING);
}

// set position
JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_seekTo(
        JNIEnv* env, jclass clazz, jint position) {
    if (NULL != uriPlayerPlay) {

        //SLuint32 state = getPlayState();
        //setPlayState(SL_PLAYSTATE_PAUSED);

        SLresult result;

        result = (*uriPlayerSeek)->SetPosition(uriPlayerSeek, position,
                SL_SEEKMODE_FAST);
        assert(SL_RESULT_SUCCESS == result);

        //setPlayState(state);
    }

}

// get duration
JNIEXPORT jint Java_com_swssm_waveloop_audio_OSLESMediaPlayer_getDuration(
        JNIEnv* env, jclass clazz) {
    if (NULL != uriPlayerPlay) {

        SLresult result;

        SLmillisecond msec;
        result = (*uriPlayerPlay)->GetDuration(uriPlayerPlay, &msec);
        assert(SL_RESULT_SUCCESS == result);

        return msec;
    }

    return 0.0f;
}

// get current position
JNIEXPORT jint Java_com_swssm_waveloop_audio_OSLESMediaPlayer_getPosition(
        JNIEnv* env, jclass clazz) {
    if (NULL != uriPlayerPlay) {

        SLresult result;

        SLmillisecond msec;
        result = (*uriPlayerPlay)->GetPosition(uriPlayerPlay, &msec);
        assert(SL_RESULT_SUCCESS == result);

        return msec;
    }

    return 0.0f;
}

//llllllllllllllllllll

JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_setPitch(
        JNIEnv* env, jclass clazz, jint rate) {
    if (NULL != uriPlaybackPitch) {
        SLresult result;

        result = (*uriPlaybackPitch)->SetPitch(uriPlaybackPitch, rate);
        assert(SL_RESULT_SUCCESS == result);
    }
}

JNIEXPORT void Java_com_swssm_waveloop_audio_OSLESMediaPlayer_setRate(
        JNIEnv* env, jclass clazz, jint rate) {
    if (NULL != uriPlaybackRate) {
        SLresult result;

        result = (*uriPlaybackRate)->SetRate(uriPlaybackRate, rate);
            assert(SL_RESULT_SUCCESS == result);


    }
}

JNIEXPORT jint Java_com_swssm_waveloop_audio_OSLESMediaPlayer_getRate(
        JNIEnv* env, jclass clazz) {
    if (NULL != uriPlaybackRate) {
        SLresult result;

        SLpermille rate;
        result = (*uriPlaybackRate)->GetRate(uriPlaybackRate, &rate);
        assert(SL_RESULT_SUCCESS == result);

        return rate;
    }

    return 0;
}

// create URI audio player
JNIEXPORT jboolean Java_com_swssm_waveloop_audio_OSLESMediaPlayer_setLoop(
        JNIEnv* env, jclass clazz, jint startPos, jint endPos) {
    SLresult result;

    result = (*uriPlayerSeek)->SetLoop(uriPlayerSeek, SL_BOOLEAN_TRUE, startPos,
            endPos);
    assert(SL_RESULT_SUCCESS == result);

    return JNI_TRUE;
}

// create URI audio player
JNIEXPORT jboolean Java_com_swssm_waveloop_audio_OSLESMediaPlayer_setNoLoop(
        JNIEnv* env, jclass clazz) {
    SLresult result;
    if (NULL != uriPlayerSeek) {
        // enable whole file looping
        result = (*uriPlayerSeek)->SetLoop(uriPlayerSeek, SL_BOOLEAN_TRUE, 0,
                SL_TIME_UNKNOWN);
        assert(SL_RESULT_SUCCESS == result);

    }
    return JNI_TRUE;
}

只需使用 ndk-build 命令编译它并使用它。如果有人成功改变音调,请告诉我解决方案。

这是 android.mk 文件

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := audio-tools

LOCAL_SRC_FILES := OSLESMediaPlayer.c


LOCAL_CFLAGS := -DHAVE_CONFIG_H -DFPM_ARM -ffast-math -O3

LOCAL_LDLIBS    += -lOpenSLES -llog

include $(BUILD_SHARED_LIBRARY)

和 Application.mk 文件

APP_STL := gnustl_static
APP_CPPFLAGS += -fexceptions -frtti
APP_ABI := armeabi armeabi-v7a

还有包装类,你可以直接在你的项目中使用它的功能

package com.swssm.waveloop.audio;
public class OSLESMediaPlayer {
    public native void createEngine();
    public native void releaseEngine();
    public native boolean createAudioPlayer(String uri);
    public native void releaseAudioPlayer();
    public native void play();
    public native void stop();
    public native void pause();
    public native boolean isPlaying();

    public native void seekTo(int position);
    public native int getDuration();
    public native int getPosition();

    public native void setPitch(int rate);

    public native void setRate(int rate);
    public native int getRate();

    public native void setLoop( int startPos, int endPos );
    public native void setNoLoop();


    public interface OnCompletionListener {
        public void OnCompletion();
    }

    private OnCompletionListener mCompletionListener;
    public void SetOnCompletionListener( OnCompletionListener listener )
    {
        mCompletionListener = listener;
    }


    private void OnCompletion()
    {
        mCompletionListener.OnCompletion();

        int position = getPosition();
        int duration = getDuration();
        if( position != duration )
        {
            int a = 0;

        }
        else
        {
            int c = 0;

        }
    }
}

关于安卓 : How to change Playback Rate of music using OpenSL ES,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11094377/

相关文章:

android - 从 ActivityGroup 开始 ActivityForResult?

c# - 有没有办法以编程方式在 android 中创建布局的副本?

objective-c - 不要播放声音

c++ - 在 C++ 中通过麦克风播放声音

python - Python:从给定时间播放音频文件

android - 如何在android中的特定时间后删除通知?

android - gradle 任务只运行最后一个命令

iphone - 如何立即播放输入声音(如回声)?

c++ - mciSendString 必须有 "wait"才能听到声音吗?

multimedia - 如何在 mp4 中编辑或添加 "rotation"的视频元数据?