Android Native C++ 层中使用AudioRecord录制PCM音频

    Android应用程序很少会用到Android Native C++ 层中AudioRecord来录制PCM,但也不是完全没有需求,至少这么做可以在不懂C++的程序员面前装装逼!
直接在Native C++层录制和处理PCM数据可以避免数据拷贝到虚拟机,再由虚拟机拷回到Native C++层;虽然这个消耗笔者认为应该不大,但是没有具体分析过虚拟机,不清楚,有知道的朋友请告诉一下。
    本文描述了在Android Native C++ 层中使用AudioRecord录制PCM音频的一般方法,希望对有此需求的童鞋有所帮助。

    C++层的AudioRecord类位于源码目录\frameworks\av\include\media\AudioRecord.h中,定义如下:

namespace android {

class audio_track_cblk_t;
// ----------------------------------------------------------------------------
class AudioRecord : virtual public RefBase
{
public:
    static const int DEFAULT_SAMPLE_RATE = 8000;    
    ...
} //end of calss
} //end of namespace android

    可以看到AudioRecord位于android名字空间中,继承于RefBase,这个基类是智能指针引用计数用的,而  
class AudioRecord : virtual public RefBase
虚继承是为了解决菱形继承问题,让从不同的路径继承过来的同一个类在子类内存中只有一个拷贝。


以下是具体代码:

#include <media/AudioRecord.h>

//==============================================
//	Audio Record Defination
//==============================================

static pthread_t		g_AudioRecordThread;
static pthread_t *	g_AudioRecordThreadPtr = NULL;

volatile bool 			g_bQuitAudioRecordThread = false;
volatile int 				g_iInSampleTime = 0;
int 								g_iNotificationPeriodInFrames = 8000/10; 
// g_iNotificationPeriodInFrames should be change when sample rate changes.

static void *	AudioRecordThread( void *inArg );
void StartAudioRecordThread();
void StopAudioRecordThread();

void AudioRecordCallback(int event, void* user, void *info)
{
	if(event == android::AudioRecord::EVENT_NEW_POS)
	{
		g_iInSampleTime += g_iNotificationPeriodInFrames;		
		//if(g_iInSampleTime > g_iNotificationPeriodInFrames*100) 
		//		g_bQuitAudioRecordThread = true;
	}
	else if (event == android::AudioRecord::EVENT_MORE_DATA)
	{		
		android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info;
        	pBuff->size = 0;
	}
	else if (event == android::AudioRecord::EVENT_OVERRUN)
	{
		LOGE(" EVENT_OVERRUN \n");
	}
}

static void *	AudioRecordThread( void *inArg )
{
	uint64_t  						inHostTime 				= 0;
	void *								inBuffer 					= NULL; 
	audio_source_t 				inputSource 			= AUDIO_SOURCE_MIC;
	audio_format_t 				audioFormat 			= AUDIO_FORMAT_PCM_16_BIT;	
	audio_channel_mask_t 	channelConfig 		= AUDIO_CHANNEL_IN_MONO; //AUDIO_CHANNEL_IN_STEREO;	
	int 									bufferSizeInBytes = 1600;
	int 									sampleRateInHz 		= 8000;	
	android::AudioRecord *	pAudioRecord 		= NULL;
	FILE * 									g_pAudioRecordFile 		= NULL;
	char 										strAudioFile[] 				= "/mnt/sdcard/external_sd/AudioRecordFile.pcm";

	int iNbChannels 		= 1;	// 1 channel for mono, 2 channel for streo
	int iBytesPerSample = 2; 	// 16bits pcm, 2Bytes
	int frameSize 			= 0;	// frameSize = iNbChannels * iBytesPerSample
  int minFrameCount 	= 0;	// get from AudroRecord object
	int iWriteDataCount = 0;	// how many data are there write to file
	
	// log the thread id for debug info
	LOGD("%s  Thread ID  = %d  \n", __FUNCTION__,  pthread_self());  
	g_iInSampleTime = 0;
	g_pAudioRecordFile = fopen(strAudioFile, "wb+");	
	
	
	iNbChannels = (channelConfig == AUDIO_CHANNEL_IN_STEREO) ? 2 : 1;
	frameSize 	= iNbChannels * iBytesPerSample;	
	
	android::status_t 	status = android::AudioRecord::getMinFrameCount(
		&minFrameCount, sampleRateInHz, audioFormat, channelConfig);	
	
	if(status != android::NO_ERROR)
	{
		LOGE("%s  AudioRecord.getMinFrameCount fail \n", __FUNCTION__);
		goto exit ;
	}
	
	LOGE("sampleRateInHz = %d minFrameCount = %d iNbChannels = %d frameSize = %d ", 
		sampleRateInHz, minFrameCount, iNbChannels, frameSize);	
	
	bufferSizeInBytes = minFrameCount * frameSize;
	
	inBuffer = malloc(bufferSizeInBytes); 
	if(inBuffer == NULL)
	{		
		LOGE("%s  alloc mem failed \n", __FUNCTION__);		
		goto exit ; 
	}

	g_iNotificationPeriodInFrames = sampleRateInHz/10;	
	
	pAudioRecord  = new android::AudioRecord();	
	if(NULL == pAudioRecord)
	{
		LOGE(" create native AudioRecord failed! ");
		goto exit;
	}
	
	pAudioRecord->set( inputSource,
                                    sampleRateInHz,
                                    audioFormat,
                                    channelConfig,
                                    0,
                                    AudioRecordCallback,
                                    NULL,
                                    0,
                                    true,
                                    0); 

	if(pAudioRecord->initCheck() != android::NO_ERROR)  
	{
		LOGE("AudioTrack initCheck error!");
		goto exit;
	}

	if(pAudioRecord->setPositionUpdatePeriod(g_iNotificationPeriodInFrames) != android::NO_ERROR)
	{
		LOGE("AudioTrack setPositionUpdatePeriod error!");
		goto exit;
	}
	
	if(pAudioRecord->start()!= android::NO_ERROR)
	{
		LOGE("AudioTrack start error!");
		goto exit;
	}	
	
	while (!g_bQuitAudioRecordThread)
	{
		inHostTime = UpTicks();
		int readLen = pAudioRecord->read(inBuffer, bufferSizeInBytes);		
		int writeResult = -1;
		
		if(readLen > 0) 
		{
			iWriteDataCount += readLen;
			if(NULL != g_pAudioRecordFile)
			{
				writeResult = fwrite(inBuffer, 1, readLen, g_pAudioRecordFile);				
				if(writeResult < readLen)
				{
					LOGE("Write Audio Record Stream error");
				}
			}			

			// write PCM data to file or other stream,implement it yourself
			writeResult = WriteAudioData(					
					g_iInSampleTime, 
					inHostTime, 
					inBuffer, 
					readLen);

			//LOGD("readLen = %d  writeResult = %d  iWriteDataCount = %d", readLen, writeResult, iWriteDataCount);			
		}
		else 
		{
			LOGE("pAudioRecord->read  readLen = 0");
		}
	}


	

		
	exit:

	if(NULL != g_pAudioRecordFile)
	{
		fflush(g_pAudioRecordFile);
		fclose(g_pAudioRecordFile);
		g_pAudioRecordFile = NULL;
	}

	if(pAudioRecord)
	{
		pAudioRecord->stop();
		delete pAudioRecord;
		pAudioRecord == NULL;
	}

	if(inBuffer)
	{
		free(inBuffer);
		inBuffer = NULL;
	}
	
	LOGD("%s  Thread ID  = %d  quit\n", __FUNCTION__,  pthread_self());
	return NULL;
}

呕心沥血所作,有用请点赞!


版权声明:本文为Romantic_Energy原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。