// ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, jobject jBuffer, jint sizeInBytes) { AudioRecord *lpRecorder = NULL; //LOGV("Entering android_media_AudioRecord_readInBuffer"); // get the audio recorder from which we'll read new audio samples lpRecorder = (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); if(lpRecorder==NULL) return 0; // direct buffer and direct access supported? long capacity = env->GetDirectBufferCapacity(jBuffer); if(capacity == -1) { // buffer direct access is not supported LOGE("Buffer direct access is not supported, can't record"); return 0; } //LOGV("capacity = %ld", capacity); jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); if(nativeFromJavaBuf==NULL) { LOGE("Buffer direct access is not supported, can't record"); return 0; } // read new data from the recorder return (jint) lpRecorder->read(nativeFromJavaBuf, capacity < sizeInBytes ? capacity : sizeInBytes); }
// ---------------------------------------------------------------------------- static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { // serialize access. Ugly, but functional. Mutex::Autolock lock(&sLock); AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField( thiz, javaAudioRecordFields.nativeCallbackCookie); // reset the native resources in the Java object so any attempt to access // them after a call to release fails. env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0); env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); // delete the AudioRecord object if (lpRecorder) { LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder); lpRecorder->stop(); delete lpRecorder; } // delete the callback information if (lpCookie) { LOGV("deleting lpCookie: %x\n", (int)lpCookie); env->DeleteGlobalRef(lpCookie->audioRecord_class); env->DeleteGlobalRef(lpCookie->audioRecord_ref); delete lpCookie; } }
// ---------------------------------------------------------------------------- static void android_media_AudioRecord_start(JNIEnv *env, jobject thiz) { AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); if (lpRecorder == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } lpRecorder->start(); }
// ---------------------------------------------------------------------------- static int android_media_AudioRecord_start(JNIEnv *env, jobject thiz) { AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); if (lpRecorder == NULL ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); return AUDIORECORD_ERROR; } return android_media_translateRecorderErrorCode(lpRecorder->start()); }
// ---------------------------------------------------------------------------- static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( thiz, javaAudioRecordFields.nativeRecorderInJavaObj); uint32_t period = 0; if (lpRecorder) { lpRecorder->getPositionUpdatePeriod(&period); return (jint)period; } else { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); return AUDIORECORD_ERROR; } }
// ---------------------------------------------------------------------------- static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, jint period) { AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( thiz, javaAudioRecordFields.nativeRecorderInJavaObj); if (lpRecorder) { return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) ); } else { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); return AUDIORECORD_ERROR; } }
// ---------------------------------------------------------------------------- static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField( thiz, javaAudioRecordFields.nativeRecorderInJavaObj); uint32_t markerPos = 0; if (lpRecorder) { lpRecorder->getMarkerPosition(&markerPos); return (jint)markerPos; } else { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); return AUDIORECORD_ERROR; } }
// ---------------------------------------------------------------------------- static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { // delete the AudioRecord object AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); if (lpRecorder) { //LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder); lpRecorder->stop(); delete lpRecorder; } // delete the callback information audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField( thiz, javaAudioRecordFields.nativeCallbackCookie); if (lpCookie) { LOGV("deleting lpCookie: %x\n", (int)lpCookie); delete lpCookie; } }
// ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes) { jbyte* recordBuff = NULL; AudioRecord *lpRecorder = NULL; // get the audio recorder from which we'll read new audio samples lpRecorder = (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); if (lpRecorder == NULL) { LOGE("Unable to retrieve AudioRecord object, can't record"); return 0; } if (!javaAudioData) { LOGE("Invalid Java array to store recorded audio, can't record"); return 0; } // get the pointer to where we'll record the audio // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); if (recordBuff == NULL) { LOGE("Error retrieving destination for recorded audio data, can't record"); return 0; } // read the new audio data from the native AudioRecord object ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, sizeInBytes > (jint)recorderBuffSize ? (jint)recorderBuffSize : sizeInBytes ); env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); return (jint) readSize; }
// ---------------------------------------------------------------------------- static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes) { jbyte* recordBuff = NULL; AudioRecord *lpRecorder = NULL; // get the audio recorder from which we'll read new audio samples lpRecorder = (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); if (lpRecorder == NULL) { LOGE("Unable to retrieve AudioRecord object, can't record"); return 0; } if (!javaAudioData) { LOGE("Invalid Java array to store recorded audio, can't record"); return 0; } // get the pointer to where we'll record the audio recordBuff = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL); if (recordBuff == NULL) { LOGE("Error retrieving destination for recorded audio data, can't record"); return 0; } // read the new audio data from the native AudioRecord object ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, sizeInBytes > (jint)recorderBuffSize ? (jint)recorderBuffSize : sizeInBytes ); env->ReleasePrimitiveArrayCritical(javaAudioData, recordBuff, 0); return (jint) readSize; }
// ---------------------------------------------------------------------------- static int android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, jint source, jint sampleRateInHertz, jint channels, jint audioFormat, jint buffSizeInBytes, jintArray jSession) { //LOGV(">> Entering android_media_AudioRecord_setup"); //LOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d", // sampleRateInHertz, audioFormat, channels, buffSizeInBytes); if (!audio_is_input_channel(channels)) { LOGE("Error creating AudioRecord: channel count is not 1 or 2."); return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; } uint32_t nbChannels = popcount(channels); // compare the format against the Java constants if ((audioFormat != javaAudioRecordFields.PCM16) && (audioFormat != javaAudioRecordFields.PCM8)) { LOGE("Error creating AudioRecord: unsupported audio format."); return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; } int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1; int format = audioFormat==javaAudioRecordFields.PCM16 ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT; if (buffSizeInBytes == 0) { LOGE("Error creating AudioRecord: frameCount is 0."); return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; } int frameSize = nbChannels * bytesPerSample; size_t frameCount = buffSizeInBytes / frameSize; if (source >= AUDIO_SOURCE_CNT) { LOGE("Error creating AudioRecord: unknown source."); return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE; } if (jSession == NULL) { LOGE("Error creating AudioRecord: invalid session ID pointer"); return AUDIORECORD_ERROR; } jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { LOGE("Error creating AudioRecord: Error retrieving session id pointer"); return AUDIORECORD_ERROR; } int sessionId = nSession[0]; env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; audiorecord_callback_cookie *lpCallbackData = NULL; AudioRecord* lpRecorder = NULL; // create an uninitialized AudioRecord object lpRecorder = new AudioRecord(); if(lpRecorder == NULL) { LOGE("Error creating AudioRecord instance."); return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; } // create the callback information: // this data will be passed with every AudioRecord callback jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { LOGE("Can't find %s when setting up callback.", kClassPathName); goto native_track_failure; } lpCallbackData = new audiorecord_callback_cookie; lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); // we use a weak reference so the AudioRecord object can be garbage collected. lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); lpRecorder->set(source, sampleRateInHertz, format, // word length, PCM channels, frameCount, 0, // flags recorderCallback,// callback_t lpCallbackData,// void* user 0, // notificationFrames, true, // threadCanCallJava) sessionId); if(lpRecorder->initCheck() != NO_ERROR) { LOGE("Error creating AudioRecord instance: initialization check failed."); goto native_init_failure; } nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { LOGE("Error creating AudioRecord: Error retrieving session id pointer"); goto native_init_failure; } // read the audio session ID back from AudioTrack in case a new session was created during set() nSession[0] = lpRecorder->getSessionId(); env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field // of the Java object env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder); // save our newly created callback information in the "nativeCallbackCookie" field // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData); return AUDIORECORD_SUCCESS; // failure: native_init_failure: env->DeleteGlobalRef(lpCallbackData->audioRecord_class); env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); delete lpCallbackData; native_track_failure: delete lpRecorder; env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0); env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; }
// ---------------------------------------------------------------------------- static int android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, jint source, jint sampleRateInHertz, jint nbChannels, jint audioFormat, jint buffSizeInBytes) { //LOGV(">> Entering android_media_AudioRecord_setup"); //LOGV("sampleRate=%d, audioFormat=%d, nbChannels=%d, buffSizeInBytes=%d", // sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes); if ((nbChannels == 0) || (nbChannels > 2)) { LOGE("Error creating AudioRecord: channel count is not 1 or 2."); return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT; } // compare the format against the Java constants if ((audioFormat != javaAudioRecordFields.PCM16) && (audioFormat != javaAudioRecordFields.PCM8)) { LOGE("Error creating AudioRecord: unsupported audio format."); return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; } int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1; int format = audioFormat==javaAudioRecordFields.PCM16 ? AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT; if (buffSizeInBytes == 0) { LOGE("Error creating AudioRecord: frameCount is 0."); return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; } int frameSize = nbChannels * bytesPerSample; size_t frameCount = buffSizeInBytes / frameSize; // compare the source against the Java constants AudioRecord::stream_type arSource; if (source == javaAudioRecordFields.SOURCE_DEFAULT) { arSource = AudioRecord::DEFAULT_INPUT; } else if (source == javaAudioRecordFields.SOURCE_MIC) { arSource = AudioRecord::MIC_INPUT; } else { LOGE("Error creating AudioRecord: unknown source."); return AUDIORECORD_ERROR_SETUP_INVALIDSTREAMTYPE; } audiorecord_callback_cookie *lpCallbackData = NULL; AudioRecord* lpRecorder = NULL; // create an uninitialized AudioRecord object lpRecorder = new AudioRecord(); if(lpRecorder == NULL) { LOGE("Error creating AudioRecord instance."); return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; } // create the callback information: // this data will be passed with every AudioRecord callback jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { LOGE("Can't find %s when setting up callback.", kClassPathName); goto native_track_failure; } lpCallbackData = new audiorecord_callback_cookie; lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); // we use a weak reference so the AudioRecord object can be garbage collected. lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); lpRecorder->set(arSource, sampleRateInHertz, format, // word length, PCM nbChannels, frameCount, 0, // flags recorderCallback,// callback_t lpCallbackData,// void* user 0, // notificationFrames, true); // threadCanCallJava) if(lpRecorder->initCheck() != NO_ERROR) { LOGE("Error creating AudioRecord instance: initialization check failed."); goto native_init_failure; } // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field // of the Java object env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)lpRecorder); // save our newly created callback information in the "nativeCallbackCookie" field // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData); return AUDIORECORD_SUCCESS; // failure: native_init_failure: delete lpCallbackData; native_track_failure: delete lpRecorder; env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0); env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; }
status_t FMA2DPWriter::readerthread() { status_t err = OK; int framecount =((4*mBufferSize)/mAudioChannels)/sizeof(int16_t); //sizeof(int16_t) is frame size for PCM stream int inChannel = (mAudioChannels == 2) ? AUDIO_CHANNEL_IN_STEREO : AUDIO_CHANNEL_IN_MONO; prctl(PR_SET_NAME, (unsigned long)"FMA2DPReaderThread", 0, 0, 0); AudioRecord* record = new AudioRecord( mAudioSource, mSampleRate, mAudioFormat, inChannel, framecount); if(!record){ ALOGE("fatal:Not able to open audiorecord"); return UNKNOWN_ERROR; } status_t res = record->initCheck(); if (res == NO_ERROR) res = record->start(); else{ ALOGE("fatal:record init check failure"); return UNKNOWN_ERROR; } while (!mDone) { mFreeQLock.lock(); if(mFreeQ.empty()){ mFreeQLock.unlock(); ALOGV("FreeQ empty"); sem_wait(&mReaderThreadWakeupsem); ALOGV("FreeQ filled up"); continue; } List<audioBufferstruct>::iterator it = mFreeQ.begin(); audioBufferstruct buff ( it->audioBuffer,it->bufferlen); mFreeQ.erase(it); mFreeQLock.unlock(); buff.bufferlen = record->read(buff.audioBuffer, mBufferSize); ALOGV("read %d bytes", buff.bufferlen); if (buff.bufferlen <= 0){ ALOGE("error in reading from audiorecord..bailing out."); this ->notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, ERROR_MALFORMED); err = INVALID_OPERATION ; break; } mDataQLock.lock(); if(mDataQ.empty()){ ALOGV("waking up reader"); sem_post(&mWriterThreadWakeupsem); } mDataQ.push_back(buff); mDataQLock.unlock(); } record->stop(); delete record; return err; }