blob: 5f6f3aa29cc5b3c2707ad7afe0bc0441a42e1a07 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "OpenSL-ES-Test-B-1-2-Recording"
#include "sl-utils.h"
/*
* See https://www.khronos.org/registry/sles/specs/OpenSL_ES_Specification_1.0.1.pdf
* Appendix B.1.2 sample code.
*
* Minor edits made to conform to Android coding style.
*
* Correction to code: SL_IID_AUDIOIODEVICECAPABILITIES is not supported.
* Detection of microphone should be made in Java layer.
*/
#define MAX_NUMBER_INTERFACES 5
#define MAX_NUMBER_INPUT_DEVICES 3
#define POSITION_UPDATE_PERIOD 1000 /* 1 sec */
static void RecordEventCallback(SLRecordItf caller __unused,
void *pContext __unused,
SLuint32 recordevent __unused)
{
/* Callback code goes here */
}
/*
* Test recording of audio from a microphone into a specified file
*/
static void TestAudioRecording(SLObjectItf sl)
{
SLObjectItf recorder;
SLRecordItf recordItf;
SLEngineItf EngineItf;
SLAudioIODeviceCapabilitiesItf AudioIODeviceCapabilitiesItf;
SLAudioInputDescriptor AudioInputDescriptor;
SLresult res;
SLDataSource audioSource;
SLDataLocator_IODevice locator_mic;
SLDeviceVolumeItf devicevolumeItf;
SLDataSink audioSink;
SLDataLocator_URI uri;
SLDataFormat_MIME mime;
int i;
SLboolean required[MAX_NUMBER_INTERFACES];
SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];
SLuint32 InputDeviceIDs[MAX_NUMBER_INPUT_DEVICES];
SLint32 numInputs = 0;
SLboolean mic_available = SL_BOOLEAN_FALSE;
SLuint32 mic_deviceID = 0;
/* Get the SL Engine Interface which is implicit */
res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void *)&EngineItf);
CheckErr(res);
AudioIODeviceCapabilitiesItf = NULL;
/* Get the Audio IO DEVICE CAPABILITIES interface, which is also
implicit */
res = (*sl)->GetInterface(sl, SL_IID_AUDIOIODEVICECAPABILITIES,
(void *)&AudioIODeviceCapabilitiesItf);
// ANDROID: obtaining SL_IID_AUDIOIODEVICECAPABILITIES may fail
if (AudioIODeviceCapabilitiesItf != NULL ) {
numInputs = MAX_NUMBER_INPUT_DEVICES;
res = (*AudioIODeviceCapabilitiesItf)->GetAvailableAudioInputs(
AudioIODeviceCapabilitiesItf, &numInputs, InputDeviceIDs);
CheckErr(res);
/* Search for either earpiece microphone or headset microphone input
device - with a preference for the latter */
for (i = 0; i < numInputs; i++) {
res = (*AudioIODeviceCapabilitiesItf)->QueryAudioInputCapabilities(
AudioIODeviceCapabilitiesItf, InputDeviceIDs[i], &AudioInputDescriptor);
CheckErr(res);
if ((AudioInputDescriptor.deviceConnection == SL_DEVCONNECTION_ATTACHED_WIRED)
&& (AudioInputDescriptor.deviceScope == SL_DEVSCOPE_USER)
&& (AudioInputDescriptor.deviceLocation == SL_DEVLOCATION_HEADSET)) {
mic_deviceID = InputDeviceIDs[i];
mic_available = SL_BOOLEAN_TRUE;
break;
}
else if ((AudioInputDescriptor.deviceConnection == SL_DEVCONNECTION_INTEGRATED)
&& (AudioInputDescriptor.deviceScope == SL_DEVSCOPE_USER)
&& (AudioInputDescriptor.deviceLocation == SL_DEVLOCATION_HANDSET)) {
mic_deviceID = InputDeviceIDs[i];
mic_available = SL_BOOLEAN_TRUE;
break;
}
}
} else {
mic_deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
mic_available = true;
}
/* If neither of the preferred input audio devices is available, no
point in continuing */
if (!mic_available) {
/* Appropriate error message here */
ALOGW("No microphone available");
return;
}
/* Initialize arrays required[] and iidArray[] */
for (i = 0; i < MAX_NUMBER_INTERFACES; i++) {
required[i] = SL_BOOLEAN_FALSE;
iidArray[i] = SL_IID_NULL;
}
// ANDROID: the following may fail for volume
devicevolumeItf = NULL;
/* Get the optional DEVICE VOLUME interface from the engine */
res = (*sl)->GetInterface(sl, SL_IID_DEVICEVOLUME,
(void *)&devicevolumeItf);
/* Set recording volume of the microphone to -3 dB */
if (devicevolumeItf != NULL) { // ANDROID: Volume may not be supported
res = (*devicevolumeItf)->SetVolume(devicevolumeItf, mic_deviceID, -300);
CheckErr(res);
}
/* Setup the data source structure */
locator_mic.locatorType = SL_DATALOCATOR_IODEVICE;
locator_mic.deviceType = SL_IODEVICE_AUDIOINPUT;
locator_mic.deviceID = mic_deviceID;
locator_mic.device= NULL;
audioSource.pLocator = (void *)&locator_mic;
audioSource.pFormat = NULL;
#if 0
/* Setup the data sink structure */
uri.locatorType = SL_DATALOCATOR_URI;
uri.URI = (SLchar *) "file:///recordsample.wav";
mime.formatType = SL_DATAFORMAT_MIME;
mime.mimeType = (SLchar *) "audio/x-wav";
mime.containerType = SL_CONTAINERTYPE_WAV;
audioSink.pLocator = (void *)&uri;
audioSink.pFormat = (void *)&mime;
#else
// FIXME: Android requires SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
// because the recorder makes the distinction from SL_DATALOCATOR_BUFFERQUEUE
// which the player does not.
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_LEFT, SL_BYTEORDER_LITTLEENDIAN
};
audioSink = { &loc_bq, &format_pcm };
#endif
/* Create audio recorder */
res = (*EngineItf)->CreateAudioRecorder(EngineItf, &recorder,
&audioSource, &audioSink, 0, iidArray, required);
CheckErr(res);
/* Realizing the recorder in synchronous mode. */
res = (*recorder)->Realize(recorder, SL_BOOLEAN_FALSE);
CheckErr(res);
/* Get the RECORD interface - it is an implicit interface */
res = (*recorder)->GetInterface(recorder, SL_IID_RECORD, (void *)&recordItf);
CheckErr(res);
// ANDROID: Should register SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface for callback.
// but does original SL_DATALOCATOR_BUFFERQUEUE variant work just as well ?
/* Setup to receive position event callbacks */
res = (*recordItf)->RegisterCallback(recordItf, RecordEventCallback, NULL);
CheckErr(res);
/* Set notifications to occur after every second - may be useful in
updating a recording progress bar */
res = (*recordItf)->SetPositionUpdatePeriod(recordItf, POSITION_UPDATE_PERIOD);
CheckErr(res);
res = (*recordItf)->SetCallbackEventsMask(recordItf, SL_RECORDEVENT_HEADATNEWPOS);
CheckErr(res);
/* Set the duration of the recording - 30 seconds (30,000
milliseconds) */
res = (*recordItf)->SetDurationLimit(recordItf, 30000);
CheckErr(res);
/* Record the audio */
res = (*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);
CheckErr(res);
// ANDROID: BUG - we don't wait for anything to record!
/* Destroy the recorder object */
(*recorder)->Destroy(recorder);
}
extern "C" void Java_android_media_cts_AudioNativeTest_nativeAppendixBRecording(
JNIEnv * /* env */, jclass /* clazz */)
{
SLObjectItf engineObject = android::OpenSLEngine();
LOG_ALWAYS_FATAL_IF(engineObject == NULL, "cannot open OpenSL ES engine");
TestAudioRecording(engineObject);
android::CloseSLEngine(engineObject);
}