Sangho Cha
2018-05-03 00:42:52 UTC
I'm developing an audio recorder app with OpenSLES on Android. I found out
something wrong in the very first part of the recorded data. Every time I
record, I get zeros only at the first part of the data. The length is
always different. I tested the app on several devices, and the length of
zero differs on each of them.
<Loading Image...
>
The codes that I tested are almost same as native-audio
<https://github.com/googlesamples/android-ndk/tree/master/native-audio> given
by Google.
The audio format I tested is a PCM, 16bit sample in 16KHz. I use 2 of a 640
bytes buffer to record, switch them in the callback function and copy the
filled one to another buffer to be written as a file after recording is
done.
I want the buffer is filled with non zero data, or at least I want to be
notified by callback or something when the non-zero data is prepared.
There could be my misunderstanding on OpenSLES. so please guide me if
anybody knows why.
I attach the codes snippet below.
// engine interfacesstatic SLObjectItf engineObject = NULL;static SLEngineItf engineEngine;
// recorder interfacesstatic SLObjectItf recorderObject = NULL;static SLRecordItf recorderRecord;static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
static char audioFilePath[256];
#define RECORDER_FRAMES (160 * 2)#define FILEOUT_BUFFER (16000 * 60)
static short* recorderBuffer = NULL;static short recorderBuffer0[RECORDER_FRAMES];static short recorderBuffer1[RECORDER_FRAMES];
static short* fileoutBuffer = NULL;static unsigned int fileoutBufferPosition = 0;
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
assert(bq == recorderBufferQueue);
assert(NULL == context);
memcpy(fileoutBuffer + fileoutBufferPosition, recorderBuffer, RECORDER_FRAMES * sizeof(short));
fileoutBufferPosition += RECORDER_FRAMES;
recorderBuffer = recorderBuffer == recorderBuffer0 ? recorderBuffer1 : recorderBuffer0;
SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recorderBuffer,
RECORDER_FRAMES * sizeof(short)); }
/*
* Class: com_example_slesrecorder_slesrecorder_NativeAudio
* Method: createAudioRecorder
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_example_slesrecorder_slesrecorder_NativeAudio_createAudioRecorder
(JNIEnv * env, jclass clazz){
SLresult result;
// configure audio source
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
// configure audio sink
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_16,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
SLDataSink audioSnk = {&loc_bq, &format_pcm};
// create audio recorder
// (requires the RECORD_AUDIO permission)
const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc,
&audioSnk, 1, id, req);
if (SL_RESULT_SUCCESS != result) {
return JNI_FALSE;
}
// realize the audio recorder
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
return JNI_FALSE;
}
// get the record interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// get the buffer queue interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&recorderBufferQueue);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// register callback on the buffer queue
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback,
NULL);
assert(SL_RESULT_SUCCESS == result);
(void)result;
return JNI_TRUE;}
/*
* Class: com_example_slesrecorder_slesrecorder_NativeAudio
* Method: startRecording
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_example_slesrecorder_slesrecorder_NativeAudio_startRecording
(JNIEnv * env, jclass clazz, jstring filepath){
SLresult result;
const char* utf8str = env->GetStringUTFChars(filepath, NULL);
strcpy(audioFilePath, utf8str);
// in case already recording, stop recording and clear buffer queue
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
assert(SL_RESULT_SUCCESS == result);
(void)result;
result = (*recorderBufferQueue)->Clear(recorderBufferQueue);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// enqueue an empty buffer to be filled by the recorder
// (for streaming recording, we would enqueue at least 2 empty buffers to start things off)
recorderBuffer = recorderBuffer0;
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recorderBuffer,
RECORDER_FRAMES * sizeof(short));
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
assert(SL_RESULT_SUCCESS == result);
(void)result;
// start recording
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
assert(SL_RESULT_SUCCESS == result);
(void)result;}
/*
* Class: com_example_slesrecorder_slesrecorder_NativeAudio
* Method: stopRecording
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_slesrecorder_slesrecorder_NativeAudio_stopRecording
(JNIEnv * env, jclass clazz){
SLresult result;
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
if (SL_RESULT_SUCCESS == result && fileoutBufferPosition != 0) {
FILE* fp = fopen(audioFilePath, "wb");
fwrite(fileoutBuffer, sizeof(short), fileoutBufferPosition, fp);
fclose(fp);
}
fileoutBufferPosition = 0;
(void)result;}
something wrong in the very first part of the recorded data. Every time I
record, I get zeros only at the first part of the data. The length is
always different. I tested the app on several devices, and the length of
zero differs on each of them.
<Loading Image...
The codes that I tested are almost same as native-audio
<https://github.com/googlesamples/android-ndk/tree/master/native-audio> given
by Google.
The audio format I tested is a PCM, 16bit sample in 16KHz. I use 2 of a 640
bytes buffer to record, switch them in the callback function and copy the
filled one to another buffer to be written as a file after recording is
done.
I want the buffer is filled with non zero data, or at least I want to be
notified by callback or something when the non-zero data is prepared.
There could be my misunderstanding on OpenSLES. so please guide me if
anybody knows why.
I attach the codes snippet below.
// engine interfacesstatic SLObjectItf engineObject = NULL;static SLEngineItf engineEngine;
// recorder interfacesstatic SLObjectItf recorderObject = NULL;static SLRecordItf recorderRecord;static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
static char audioFilePath[256];
#define RECORDER_FRAMES (160 * 2)#define FILEOUT_BUFFER (16000 * 60)
static short* recorderBuffer = NULL;static short recorderBuffer0[RECORDER_FRAMES];static short recorderBuffer1[RECORDER_FRAMES];
static short* fileoutBuffer = NULL;static unsigned int fileoutBufferPosition = 0;
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
assert(bq == recorderBufferQueue);
assert(NULL == context);
memcpy(fileoutBuffer + fileoutBufferPosition, recorderBuffer, RECORDER_FRAMES * sizeof(short));
fileoutBufferPosition += RECORDER_FRAMES;
recorderBuffer = recorderBuffer == recorderBuffer0 ? recorderBuffer1 : recorderBuffer0;
SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recorderBuffer,
RECORDER_FRAMES * sizeof(short)); }
/*
* Class: com_example_slesrecorder_slesrecorder_NativeAudio
* Method: createAudioRecorder
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_example_slesrecorder_slesrecorder_NativeAudio_createAudioRecorder
(JNIEnv * env, jclass clazz){
SLresult result;
// configure audio source
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
// configure audio sink
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_16,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
SLDataSink audioSnk = {&loc_bq, &format_pcm};
// create audio recorder
// (requires the RECORD_AUDIO permission)
const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc,
&audioSnk, 1, id, req);
if (SL_RESULT_SUCCESS != result) {
return JNI_FALSE;
}
// realize the audio recorder
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
return JNI_FALSE;
}
// get the record interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// get the buffer queue interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&recorderBufferQueue);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// register callback on the buffer queue
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback,
NULL);
assert(SL_RESULT_SUCCESS == result);
(void)result;
return JNI_TRUE;}
/*
* Class: com_example_slesrecorder_slesrecorder_NativeAudio
* Method: startRecording
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_example_slesrecorder_slesrecorder_NativeAudio_startRecording
(JNIEnv * env, jclass clazz, jstring filepath){
SLresult result;
const char* utf8str = env->GetStringUTFChars(filepath, NULL);
strcpy(audioFilePath, utf8str);
// in case already recording, stop recording and clear buffer queue
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
assert(SL_RESULT_SUCCESS == result);
(void)result;
result = (*recorderBufferQueue)->Clear(recorderBufferQueue);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// enqueue an empty buffer to be filled by the recorder
// (for streaming recording, we would enqueue at least 2 empty buffers to start things off)
recorderBuffer = recorderBuffer0;
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recorderBuffer,
RECORDER_FRAMES * sizeof(short));
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
assert(SL_RESULT_SUCCESS == result);
(void)result;
// start recording
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
assert(SL_RESULT_SUCCESS == result);
(void)result;}
/*
* Class: com_example_slesrecorder_slesrecorder_NativeAudio
* Method: stopRecording
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_slesrecorder_slesrecorder_NativeAudio_stopRecording
(JNIEnv * env, jclass clazz){
SLresult result;
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
if (SL_RESULT_SUCCESS == result && fileoutBufferPosition != 0) {
FILE* fp = fopen(audioFilePath, "wb");
fwrite(fileoutBuffer, sizeof(short), fileoutBufferPosition, fp);
fclose(fp);
}
fileoutBufferPosition = 0;
(void)result;}
--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+***@googlegroups.com.
To post to this group, send email to android-***@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.
To view this discussion on the web visit https://groups.google.com/d/msgid/android-ndk/2b044763-80a8-4900-b404-52f757fd2cc3%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+***@googlegroups.com.
To post to this group, send email to android-***@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.
To view this discussion on the web visit https://groups.google.com/d/msgid/android-ndk/2b044763-80a8-4900-b404-52f757fd2cc3%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.