Add sound effects to save sdcard

#1

Hello,

I am using OpenSL ES for my android app and implemented it already.But I have a problem.

1.open a file from sdcard.
2.add effect(reverb).
3.save to sdcard.
4.play the music with effect file from sdcard.

Thank you in advance!
Thanks for your help.

this is jni file.

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#define ANDROID

#include <SLES/OpenSLES.h>
#ifdef ANDROID
#include <SLES/OpenSLES_Android.h>
#endif

#include <jni.h>
#include <sys/types.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/log.h>
#define MAX_NUMBER_INTERFACES 4

#define TIME_S_BETWEEN_SETTING_CHANGE 3

//-----------------------------------------------------------------
/* Exits the application if an error is encountered */
#define ExitOnError(x) ExitOnErrorFuncReverb(x,LINE)

void ExitOnErrorFuncReverb(SLresult result, int line) {
if (SL_RESULT_SUCCESS != result) {
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"%u error code encountered at line %d, exiting
", result,
line);
exit (EXIT_FAILURE);
}
}

// Prefetch status callback

#define PREFETCHEVENT_ERROR_CANDIDATE
(SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE)

SLboolean errorInPrefetchCallback = SL_BOOLEAN_FALSE;

void prefetch_callback(SLPrefetchStatusItf caller, void *context,
SLuint32 event) {
SLresult result;
assert(context == NULL);
SLpermille level;
result = (*caller)->GetFillLevel(caller, &level);
ExitOnError(result);
SLuint32 status;
result = (*caller)->GetPrefetchStatus(caller, &status);
ExitOnError(result);
if ((PREFETCHEVENT_ERROR_CANDIDATE
== (event & PREFETCHEVENT_ERROR_CANDIDATE))
&& (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)){
errorInPrefetchCallback = SL_BOOLEAN_TRUE;
}
}

//-----------------------------------------------------------------

/* Play an audio path and feed a global reverb  */

void TestSendToPresetReverb(SLObjectItf sl, const char* path, int preset,
SLmillibel directLevel, SLmillibel sendLevel, bool alwaysOn, bool useFd,
bool loop) {
SLresult result;
SLEngineItf EngineItf;

/* Objects this application uses: one player and an ouput mix */
SLObjectItf player, outputMix;

/* Source of audio data to play */
SLDataSource audioSource;

#ifdef ANDROID
SLDataLocator_AndroidFD locatorFd;
#endif
SLDataLocator_URI locatorUri;
SLDataFormat_MIME mime;

/* Data sinks for the audio player */
SLDataSink audioSink;
SLDataLocator_OutputMix locator_outputmix;

/* Interfaces for the audio player */
SLPlayItf playItf;
SLPrefetchStatusItf prefetchItf;
SLEffectSendItf effectSendItf;
SLSeekItf seekItf;

/* Interface for the output mix */
SLPresetReverbItf reverbItf;

SLboolean required[MAX_NUMBER_INTERFACES];
SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];

/* Get the SL Engine Interface which is implicit */
result = (*sl)-&gt;GetInterface(sl, SL_IID_ENGINE, (void*) &EngineItf);
ExitOnError(result);

/* ------------------------------------------------------------------------------------------------ */
/*
 /* Create Output Mix object to be used by the player
 /*
 /* ------------------------------------------------------------------------------------------------ */
/* Initialize arrays required[] and iidArray[] */
for (int i = 0; i &lt; MAX_NUMBER_INTERFACES; i++) {
	required[i] = SL_BOOLEAN_FALSE;
	iidArray[i] = SL_IID_NULL;
}
/* Set arrays required[] and iidArray[] for required interfaces */
required[0] = SL_BOOLEAN_TRUE;
iidArray[0] = SL_IID_PRESETREVERB;

result = (*EngineItf)-&gt;CreateOutputMix(EngineItf, &outputMix, 1, iidArray,
		required);
ExitOnError(result);

/* Realize the Output Mix object in synchronous mode */
result = (*outputMix)-&gt;Realize(outputMix, SL_BOOLEAN_FALSE );
ExitOnError(result);

/* Get the SLPresetReverbItf for the output mix */
result = (*outputMix)-&gt;GetInterface(outputMix, SL_IID_PRESETREVERB,
		(void*) &reverbItf);
ExitOnError(result);

/* ------------------------------------------------------------------------------------------------ */
/*
 /*Setup the data sink structure
 /*SLDataLocator_OutputMix
 /* ------------------------------------------------------------------------------------------------ */
locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
locator_outputmix.outputMix = outputMix;
audioSink.pLocator = (void*) &locator_outputmix;
audioSink.pFormat = NULL;

result = (*reverbItf)-&gt;SetPreset(reverbItf, preset);
ExitOnError(result);

/* ------------------------------------------------------------------------------------------------ */
/*
 /* Configuration SLDataSource
 * SLDataLocator_URI
 *	SLDataLocator_AndroidFD
 /* ------------------------------------------------------------------------------------------------ */
//audiosource pLocator
locatorUri.locatorType = SL_DATALOCATOR_URI;
locatorUri.URI = (SLchar *) path;
audioSource.pLocator = (void*) &locatorUri;
if (useFd) {
	/* Setup the data source structure for the URI */
	locatorFd.locatorType = SL_DATALOCATOR_ANDROIDFD;
	int fd = open(path, O_RDONLY);
	if (fd == -1) {
		perror(path);
		exit (EXIT_FAILURE);
	}
	locatorFd.fd = (SLint32) fd;
	locatorFd.length = SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE;
	locatorFd.offset = 0;
	audioSource.pLocator = (void*) &locatorFd;
}

//audiosource format
mime.formatType = SL_DATAFORMAT_MIME;
/*     this is how ignored mime information is specified, according to OpenSL ES spec
 *     in 9.1.6 SLDataFormat_MIME and 8.23 SLMetadataTraversalItf GetChildInfo */
mime.mimeType = (SLchar*) NULL;
mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
audioSource.pFormat = (void*) &mime;

/* ------------------------------------------------------------------------------------------------ */
/*
 /* Create the audio player
 /* ------------------------------------------------------------------------------------------------ */
/* Set arrays required[] and iidArray[] for required interfaces */
/*  (SLPlayItf is implicit) */
required[0] = SL_BOOLEAN_TRUE;
iidArray[0] = SL_IID_PREFETCHSTATUS;
required[1] = SL_BOOLEAN_TRUE;
iidArray[1] = SL_IID_EFFECTSEND;
required[2] = SL_BOOLEAN_TRUE;
iidArray[2] = SL_IID_SEEK;

result = (*EngineItf)-&gt;CreateAudioPlayer(EngineItf, &player, &audioSource,
		&audioSink, 3, iidArray, required);
ExitOnError(result);

/* Realize the player in synchronous mode. */
result = (*player)-&gt;Realize(player, SL_BOOLEAN_FALSE );
ExitOnError(result);
__android_log_print(ANDROID_LOG_INFO, "audio_effect",
		"URI example: after Realize

");

/* Get the SLPlayItf, SLPrefetchStatusItf and SLEffectSendItf interfaces for the player*/
result = (*player)-&gt;GetInterface(player, SL_IID_PLAY, (void*) &playItf);
ExitOnError(result);

result = (*player)-&gt;GetInterface(player, SL_IID_PREFETCHSTATUS,
		(void*) &prefetchItf);
ExitOnError(result);
result = (*prefetchItf)-&gt;RegisterCallback(prefetchItf, prefetch_callback,
		NULL);
ExitOnError(result);
result = (*prefetchItf)-&gt;SetCallbackEventsMask(prefetchItf,
		SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE );
ExitOnError(result);
result = (*player)-&gt;GetInterface(player, SL_IID_EFFECTSEND,
		(void*) &effectSendItf);
ExitOnError(result);

result = (*player)-&gt;GetInterface(player, SL_IID_SEEK, (void*) &seekItf);
ExitOnError(result);

__android_log_print(ANDROID_LOG_INFO, "audio_effect",
		"Player configured

");

/* ------------------------------------------------------ */
/* Playback and test */

/* Start the data prefetching by setting the player to the paused state */
result = (*playItf)-&gt;SetPlayState(playItf, SL_PLAYSTATE_PAUSED );
ExitOnError(result);

/* Wait until there's data to play */
SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
while (prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA ) {
	if (errorInPrefetchCallback) {
		__android_log_print(ANDROID_LOG_INFO, "audio_effect",
				"Error during prefetch, exiting

");
exit (EXIT_FAILURE);
}
usleep(100 * 1000);
(*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus);
ExitOnError(result);
}

/* Get duration */
SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
result = (*playItf)-&gt;GetDuration(playItf, &durationInMsec);
ExitOnError(result);
if (durationInMsec == SL_TIME_UNKNOWN ) {
	__android_log_print(ANDROID_LOG_INFO, "audio_effect",
			"Duration unknown, assuming 10 seconds

");
durationInMsec = 10000;
} else {
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"Duration is %.1f seconds
", durationInMsec / 1000.0);
}

/* Feed the output mix' reverb from the audio player using the given send level */
result = (*effectSendItf)-&gt;EnableEffectSend(effectSendItf, reverbItf,
		SL_BOOLEAN_TRUE, sendLevel);
ExitOnError(result);

result = (*effectSendItf)-&gt;SetDirectLevel(effectSendItf, directLevel);
ExitOnError(result);
__android_log_print(ANDROID_LOG_INFO, "audio_effect",
		"Set direct level to %dmB

", directLevel);

result = (*effectSendItf)-&gt;SetSendLevel(effectSendItf, reverbItf,
		sendLevel);
ExitOnError(result);
__android_log_print(ANDROID_LOG_INFO, "audio_effect",
		"Set send level to %dmB

", sendLevel);

/* Enable looping */
if (loop) {
	result = (*seekItf)-&gt;SetLoop(seekItf, SL_BOOLEAN_TRUE,
			(SLmillisecond) 0, SL_TIME_UNKNOWN );
	ExitOnError(result);
}
result = (*player)-&gt;GetInterface(player, SL_IID_PLAY, (void*) &playItf);

/* Start playback */
result = (*playItf)-&gt;SetPlayState(playItf, SL_PLAYSTATE_PLAYING );
ExitOnError(result);

/* Disable preset reverb every TIME_S_BETWEEN_SETTING_CHANGE seconds unless always on */
SLboolean previousEnabled = SL_BOOLEAN_FALSE;
SLuint32 playState;
for (;;) {
	result = (*playItf)-&gt;GetPlayState(playItf, &playState);
	ExitOnError(result);
	if (playState != SL_PLAYSTATE_PLAYING )
		break;
	SLboolean enabled;
	enabled = alwaysOn || !previousEnabled;
	if (enabled != previousEnabled) {
		result = (*reverbItf)-&gt;SetPreset(reverbItf,
				enabled ? preset : SL_REVERBPRESET_NONE );
		__android_log_print(ANDROID_LOG_INFO, "audio_effect",
				"SetPreset(%d)=%d

",
enabled ? preset : SL_REVERBPRESET_NONE, result);
ExitOnError(result);
previousEnabled = enabled;
if (enabled) {
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"Reverb on
");
} else {
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"Reverb off
");
}
}
usleep(TIME_S_BETWEEN_SETTING_CHANGE * 1000 * 1000);
}

/* Make sure player is stopped */
assert(playState == SL_PLAYSTATE_STOPPED );

#if 0
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,"Stopping playback
");
result = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
ExitOnError(result);
#endif

/* Destroy the player */
(*player)-&gt;Destroy(player);

/* Destroy Output Mix object */
(*outputMix)-&gt;Destroy(outputMix);

#ifdef ANDROID
if (useFd)
close(locatorFd.fd);
#endif
}

extern “C” {
int Java_com_example_nativeaudio_NativeAudio_sendToPresetReverb(JNIEnv* env,
jclass clazz, jstring fileDirectory, jstring fileName) {

const char *directory = env-&gt;GetStringUTFChars(fileDirectory, 0);
const char *name = env-&gt;GetStringUTFChars(fileName, 0);

const char *programName = directory;

SLresult result;
SLObjectItf sl;

__android_log_print(ANDROID_LOG_INFO, "audio_effect",
		"OpenSL ES test %s: exercises SLEffectSendItf ", programName);
__android_log_print(ANDROID_LOG_INFO, "audio_effect",
		"on AudioPlayer and SLPresetReverbItf on OutputMix.

");
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"Plays the sound file designated by the given path, ");
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"and sends a specified amount of energy to a global reverb
");
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"(sendLevel in mB), with a given direct level (in mB).
");
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"Every %d seconds, the reverb is turned on and off,
",
TIME_S_BETWEEN_SETTING_CHANGE);
__android_log_print(ANDROID_LOG_INFO, “audio_effect”,
"unless the --always-on option is specified before the path.
");

bool alwaysOn = true;
bool useFd = true;
bool loop = false;

SLEngineOption EngineOption[] = { { (SLuint32) SL_ENGINEOPTION_THREADSAFE,
		(SLuint32) SL_BOOLEAN_TRUE } };

result = slCreateEngine(&sl, 1, EngineOption, 0, NULL, NULL);
ExitOnError(result);

/* Realizing the SL Engine in synchronous mode. */
result = (*sl)-&gt;Realize(sl, SL_BOOLEAN_FALSE );
ExitOnError(result);

// intentionally not checking that levels are of correct value
TestSendToPresetReverb(sl, name, (SLmillibel) (SL_REVERBPRESET_MEDIUMHALL ),
		(SLmillibel) (-1000), (SLmillibel) (-1000), alwaysOn, useFd, loop);

/* Shutdown OpenSL ES */
(*sl)-&gt;Destroy(sl);

return EXIT_SUCCESS;

}
}

#2

Hi RickLxl,

Could you be more specific as to the nature of your problem? What is working and what is not working for you?

Best,

Erik

#3

[QUOTE=Erik;30794]Hi RickLxl,

Could you be more specific as to the nature of your problem? What is working and what is not working for you?

Best,

Erik[/QUOTE]

Hi Erik.

First of all thank you for your reply.

working :record and EFFECTSEND save to sdcard(OK)
not working:read a file from sdcard. add effect and generate a new file to save sdcard.

What should I do ?

Thank you in advance!
Thanks for your help.

#4

Hi RickLxl,

I believe you have run into one of the limitations of the OpenSL ES implementation.

From what I understand you can play a file from the SD card, add the effect and send it to the speakers. You can also add an effect to incoming audio and save it to the SD card.

The use case you are describing is the transcribing use case which was not defined until OpenSL ES 1.1, and Android only supports OpenSL ES 1.0.1.

There is a possible workaround for this using buffer queues. Buffer queues were redesigned for OpenSL ES 1.1, but they are available in Android as an extension. From what I understand the Android buffer queues are similar to those in the 1.1 specification. In principle, you need to set up a player to read from the SDCard with the data sink pointing to a buffer queue. You then set up a recorder with the data source pointing to a second buffer queue and you hand the buffers from one buffer queue to the other (you may need to copy the data). Not an elegant solution, but it should work. Note that this solution is device/implementation dependent.

Good Luck!

Erik