2012-06-19 189 views
17

我正在製作一個音樂播放器,我需要在不改變音高的情況下更改速度(音樂的播放速度)。Android:如何更改使用OpenSL ES的音樂播放速率

我無法找到任何原生的android類來這樣做。我嘗試過SoundPool,但它不適用於大型音樂文件,它似乎也不適用於許多設備。我也試過AudioTrack,但再次沒有運氣。

現在我正在嘗試使用OpenSL ES處理音樂的android NDK音頻示例。現在我只想在此示例中添加設置播放速率功能。

任何人都可以告訴我如何添加更改播放速率功能嗎?

回答

30

我已經解決了我的問題。這是在任何人的情況下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; 

     } 
    } 
} 
+0

Thanx分享你的知識。你能告訴我,它將與.Mp3文件一起工作嗎?我可以使用這個庫和代碼來改變.mp3文件的速度。 Thanx in advanced vipul – Harsh

+1

是的。它可以處理各種MP3,WAV等文件格式。但不幸的是,節奏改變功能在ICS上不起作用。我不知道爲什麼,但是此功能已從Android ICS中的OpenSL ES中刪除。 –

+0

Thanx。在你的代碼片段中,你包含4個文件(#include #include #include #include ) 我在哪裏可以找到這些文件。 – Harsh

5

這可能是一些幫助(從NDK OpenSL資料爲準):

播放速率

支持的播放速率範圍(S)和功能可以根據平臺版本和實現改變 ,因此應該在運行時通過查詢PlaybackRate :: GetRateRange或 PlaybackRate :: GetCapabilitiesOfRate確定 。

這就是說,上 典型速率的範圍提供一些指導可能是有用的:在Android 2.3的單個回放 率範圍從500每千次至2000千分數包容通常是 支撐,屬性SL_RATEPROP_NOPITCHCORAUDIO。在Android 4.0 中,PCM 格式的數據源通常支持相同的速率範圍,其他格式的統一速率範圍通常也支持相同的速率範圍。