Android: как изменить скорость воспроизведения музыки с помощью OpenSL ES

Я работаю над музыкальным проигрывателем, в котором мне нужно изменить темп (скорость воспроизведения музыки) без изменения высоты тона.

Я не могу найти какой-либо собственный класс Android для этого. Я попробовал SoundPool, но он не работает с большими музыкальными файлами, а также не работает на многих устройствах. Я также пробовал AudioTrack, но снова не повезло.

Теперь я пробую аудио-пример Android NDK, который использует OpenSL ES для обработки музыки. Теперь я просто хочу добавить функцию установки скорости воспроизведения в этом примере.

Может ли кто-нибудь показать мне, как добавить в него функцию изменения скорости воспроизведения?


person Vipul Purohit    schedule 19.06.2012    source источник


Ответы (2)


Я решил свою проблему. Вот мой полный нативный код для 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;

        }
    }
}
person Vipul Purohit    schedule 29.06.2012
comment
Спасибо, что поделились своими знаниями. Можете ли вы сказать мне, будет ли он работать с файлами .Mp3. Могу ли я использовать эту библиотеку и код для изменения темпа файлов .mp3. Спасибо в расширенном Vipul - person Harsh; 04.08.2012
comment
да. Он будет работать со всеми форматами файлов MP3, WAV и т. д. Но, к сожалению, функции изменения темпа не будут работать на ICS. Я не знаю почему, но эта функция была удалена из OpenSL ES в Android ICS. - person Vipul Purohit; 06.08.2012
comment
Спасибо. В вашем фрагменте кода вы включаете 4 файла (#include ‹SLES/OpenSLES.h› #include ‹SLES/OpenSLES_Android.h› #include ‹assert.h› #include ‹sys/types.h›) Где я могу их найти? файлы. - person Harsh; 06.08.2012
comment
Это OpenSL ES — встроенная библиотека Android, поэтому вам не нужно включать какие-либо внешние файлы для их использования. - person Vipul Purohit; 06.08.2012
comment
Я последовал вашим предложениям, но столкнулся с проблемами, когда команда сборки ndk не выполнялась. Итак, я искал информацию об openssl и обнаружил, что поддерживается на уровне API 9 и выше (проверьте здесь ссылка ). Но я разрабатываю свое приложение на уровне API 8. Что мне делать? - person Harsh; 06.08.2012
comment
Выхода нет. Вы должны использовать уровень API 9, так как OpenSL ES был представлен на уровне API 9. Если вы не хотите использовать OpenSL ES, используйте библиотеку FFMPEG. Но гораздо сложнее использовать OpenSL ES. - person Vipul Purohit; 06.08.2012
comment
Привет снова, vipul. Я нашел только тебя в переполнении стека, который ведет меня в правильном направлении ... С твоим советом я передумал использовать ffmpeg. Я пытаюсь скомпилировать ffmpeg с помощью ndk, и, поскольку это мой первый раз с Android ndk, мне кажется довольно сложным скомпилировать ffmpeg для Android. И вы уверены, что эта библиотека ffmpeg может помочь мне изменить темп mp3-файлов. Если да то как. Вы можете помочь - person Harsh; 18.08.2012
comment
Я забыл сказать вам, что я пытаюсь скомпилировать его в Windows - person Harsh; 18.08.2012
comment
Привет, vipul. Я изменил версию своего приложения на 2.3, чтобы использовать openssl. И с вашей помощью я скомпилировал openssl es (с вашим кодом, опубликованным выше). Теперь, пожалуйста, расскажите мне, как использовать собственный метод setrate для изменения темпа моего mp3-файла, который находится в необработанной папке. Спасибо Vipul за помощь. - person Harsh; 29.08.2012
comment
Здравствуйте, сначала используйте метод createEngine() для создания движка. Затем используйте createAudioPlayer (FILE PATH); чтобы открыть файл, а затем вы можете использовать setPitch (int rate); или setRate (целая скорость); функция вместе с play();. - person Vipul Purohit; 30.08.2012
comment
Привет, Випул, мне нужно установить реверберацию и тон [скорость воспроизведения] для файла проигрывателя, поэтому, пожалуйста, помогите мне, я новичок в этих вещах. Я пробовал код: eReverb = new EnvironmentalReverb(0, 0); eReverb.setDecayHFRatio((короткий) 1000); eReverb.setDecayTime(10000); eReverb.setDensity((короткий) 1000); eReverb.setDiffusion((короткий) 1000); eReverb.setReverbLevel((короткий) 1000); eReverb.setReverbDelay(100); eReverb.setEnabled (истина); eReverb.setReflectionsDelay(100); mMediaPlayer.attachAuxEffect(eReverb.getId()); но не работает..... Это правильно? я пробовал с - person Ram; 21.06.2013

Это может помочь (взято из документации NDK OpenSL):

Скорость воспроизведения

Поддерживаемые диапазоны скоростей воспроизведения и возможности могут различаться в зависимости от версии и реализации платформы, поэтому их следует определять во время выполнения путем запроса с помощью PlaybackRate::GetRateRange или PlaybackRate::GetCapabilitiesOfRate.

Тем не менее, некоторые рекомендации по типичным диапазонам скорости могут быть полезны: в Android 2.3 обычно поддерживается один диапазон скорости воспроизведения от 500 до 2000 промилле включительно со свойством SL_RATEPROP_NOPITCHCORAUDIO. В Android 4.0 один и тот же диапазон скоростей обычно поддерживается для источника данных в формате PCM и единый диапазон скоростей для других форматов.

person Michael J Petrie    schedule 25.05.2013