Android : 如何使用 OpenSL ES 更改音乐的播放速率
Posted
技术标签:
【中文标题】Android : 如何使用 OpenSL ES 更改音乐的播放速率【英文标题】:Android : How to change Playback Rate of music using OpenSL ES 【发布时间】:2012-06-21 02:28:51 【问题描述】:我正在开发一个音乐播放器,我需要在不改变音高的情况下改变节奏(音乐的播放速度)。
我无法找到任何本机 android 类来执行此操作。我尝试了 SoundPool,但它不适用于大型音乐文件,而且它似乎也不适用于许多设备。我也尝试了 AudioTrack,但再次没有运气。
现在我正在尝试使用 OpenSL ES 处理音乐的 android NDK 音频示例。现在我只想在此示例中添加设置播放速率功能。
谁能告诉我如何在其中添加更改播放速率功能?
【问题讨论】:
【参考方案1】:我已经解决了我的问题。如果有人需要,这是我完整的 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;
【讨论】:
感谢分享您的知识。你能告诉我它是否适用于 .Mp3 文件。我可以使用这个库和代码来改变 .mp3 文件的速度吗?高级 vipul 中的谢谢 是的。它适用于所有类型的 MP3、WAV 等文件格式。但不幸的是,节奏改变功能不适用于 ICS。我不知道为什么,但这个功能已从 Android ICS 中的 OpenSL ES 中删除。 谢谢。在您的代码 sn-p 中,您包含 4 个文件(#include这可能会有所帮助(取自 NDK OpenSL 文档):
播放率
支持的播放速率范围和功能可能会有所不同 取决于平台版本和实现,因此应该是 在运行时通过查询 PlaybackRate::GetRateRange 或 PlaybackRate::GetCapabilitiesOfRate。
也就是说,一些关于 典型的速率范围可能有用:在 Android 2.3 中,单次播放 费率范围从 500 per mille 到 2000 per mille 包括在内 支持,具有属性 SL_RATEPROP_NOPITCHCORAUDIO。在安卓 4.0 中 PCM 中的数据源通常支持相同的速率范围 格式,以及其他格式的统一速率范围。
【讨论】:
以上是关于Android : 如何使用 OpenSL ES 更改音乐的播放速率的主要内容,如果未能解决你的问题,请参考以下文章
Android音视频十三OpenSL ES介绍&基于OpenSL ES实现音频采集
Android音视频十三OpenSL ES介绍&基于OpenSL ES实现音频采集
Android音视频十三OpenSL ES介绍&基于OpenSL ES实现音频采集