⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 recorder.c

📁 Linux下控制声卡录音的类。Linux平台下使用
💻 C
字号:
/*File Name: Recorder.c*/
#include <fcntl.h>
#include <linux/soundcard.h>
#include <sys/ioctl.h>
#include <pthread.h>

#include "Ldb.h"
#include "Recorder.h"
#include "Adpcm.h"
#include "Utility.h"
#include "LocoSystem.h"
#include "SoundDesc.h"
#include "ComMonitor.h"
#include "LocoLog.h"

#define ADPCM_COMPRESSION 1

/********************************************/
#define SAMPLE_BIT			16
#define SAMPLE_CHANNEL		1

int SAMPLE_RATE;
int SAMPLE_PER_BLOCK;
int BLOCK_ALIGN;
int SAMPLE_BUFFER_SIZE;
int CODE_BUFFER_SIZE;

/********************************************
				读写的文件及buffer
********************************************/
/*设备fd*/
int f_audiofd;

/*当前正在录音的文件*/
FILE *f_pCurrFile; 
char f_prevFileName[SYS_PATH_MAX_LEN];
char f_currFileName[SYS_PATH_MAX_LEN];
/*当前录音文件得长度*/
int f_rcdSize;

/*读写文件buffer*/
unsigned char* f_bufSample;
unsigned char* f_bufCode;

/********************************************
				录音线程
********************************************/
/*录音线程*/
pthread_t f_rcdThrd;
/*录音停止标志*/
volatile char f_rcdStop;

pthread_mutex_t f_pauseMutex = PTHREAD_MUTEX_INITIALIZER;
#define pause_Lock {pthread_mutex_lock(&f_pauseMutex);}
#define pause_Unlock {pthread_mutex_unlock(&f_pauseMutex);}
/*录音暂停标志*/
volatile char f_rcdPause;
volatile char f_forceStop;

/*录音线程函数*/
void *loco_recorder(void *arg);
void loco_rcdPlayFile(FILE *pfile);
void loco_rcdStop();
int loco_rcdStart();

/*******************************************
			读写方式打开声音设备
mode: O_WRONLY, O_RDONLY, O_RDWR
*******************************************/
int loco_openAudioDev(int mode)
{
	int status, arg;
	
	f_audiofd = open(LOCO_SOUND_DEV, mode);
	
	if(f_audiofd < 0)
	{
		LDB_ERR_MSGF("open audio device failed!");
		return -1;
	}
 
	arg = AFMT_U16_BE;
	status= ioctl(f_audiofd, SNDCTL_DSP_SETFMT, &arg);
	if(status == -1)
	{
		LDB_ERR_MSGF("AFMT_U16_BE error!");
		return -1;
	}
	else if(arg != AFMT_U16_BE)
	{
		LDB_ERR_MSGF("Failed to set sample format %d! arg = %d", AFMT_U16_BE, arg);
		return -1;
	}
	else
	{
		//printf("Set sample bits sucessfully\r\n");
	}

	arg = SAMPLE_BIT;
	status= ioctl(f_audiofd, SNDCTL_DSP_SAMPLESIZE, &arg);
	if(status == -1)
	{
		LDB_ERR_MSGF("SNDCTL_DSP_SAMPLESIZE error!");
		return -1;
	}
	else if(arg != SAMPLE_BIT)
	{
		LDB_ERR_MSGF("Failed to set sample bits!arg = %d", arg);
		return -1;
	}
	else
	{
		//printf("Set sample bits sucessfully\r\n");
	}

	arg = SAMPLE_CHANNEL;
	status= ioctl(f_audiofd, SNDCTL_DSP_CHANNELS, &arg);
	if(status == -1)
	{
		LDB_ERR_MSGF("SNDCTL_DSP_CHANNELS error!");
		return -1;	
	}
	else if(arg != SAMPLE_CHANNEL)
	{
		LDB_ERR_MSGF("unable to set numble of channels!arg = %d",arg);
		return -1;
	}
	else
	{
		//printf("Set channels sucessfully\r\n");
	}
	
	arg = SAMPLE_RATE;
	status= ioctl(f_audiofd, SNDCTL_DSP_SPEED, &arg);
	if(status == -1)
	{
		LDB_ERR_MSGF("SNDCTL_DSP_SPEED error!");
		return -1;	
	}
	else if(arg != SAMPLE_RATE)
	{
		LDB_ERR_MSGF("unable to set rate:%d! arg = %d",SAMPLE_RATE,arg);
		return -1;
	}
	else
	{
		//printf("Set sample rate sucessfully\r\n");
	}

	return f_audiofd;
}

int loco_dspRead(unsigned char *buffer, int size)
{
	int nread;
	if (f_audiofd == -1)
	{
		return -1;
	}

	nread = read(f_audiofd, buffer, size);

	return nread; 
}

int loco_dspWrite(unsigned char *buffer, int size)
{
	int nwrite;
	if (f_audiofd == -1)
	{
		return -1;
	}

	nwrite = write(f_audiofd, buffer, size);

	return nwrite; 
}

void loco_dspClose()
{
	if (f_audiofd != -1)
	{
		close(f_audiofd);
		f_audiofd = -1;
	}
	printf("dsp closed \n");
}
/*******************************************/
int  loco_rcdInit()
{
	if (g_sysSampleRate == 8)
	{
		SAMPLE_RATE = 8000;
		SAMPLE_PER_BLOCK = 505;
		BLOCK_ALIGN = 256;
		SAMPLE_BUFFER_SIZE = 2020;
		CODE_BUFFER_SIZE = 512;
	}
	else
	{
		SAMPLE_RATE = 16000;
		SAMPLE_PER_BLOCK = 1017;
		BLOCK_ALIGN = 512;
		SAMPLE_BUFFER_SIZE = 4068;
		CODE_BUFFER_SIZE = 1024;
	}
	
	f_forceStop = 0;
	/*分配工作buffer*/
	f_bufSample = (unsigned char*)malloc(SAMPLE_BUFFER_SIZE);
	f_bufCode = (unsigned char*)malloc(CODE_BUFFER_SIZE);
	if (NULL == f_bufSample || NULL == f_bufCode)
		return -1;

	return loco_rcdStart();
}

void loco_rcdFree()
{
	loco_rcdStop();

	free(f_bufSample);
	f_bufSample = NULL;

	free(f_bufCode);
	f_bufCode = NULL;

	f_forceStop = 0;
}

int loco_rcdStart()
{
	f_pCurrFile = NULL;
	
	if (g_rcdAuto == 1)
		f_rcdPause = 0;
	else
		f_rcdPause = 1;
	
	f_audiofd = -1;

	/*创建语音保存文件夹*/
	if (ldb_fileCreateDir(SYS_SOUND_SAVE_DIR) == -1)
	{
		LDB_ERR_MSGF("can not create recorder directory");
		return -1;
	}

	/*打开声音设备*/
	if (-1 == loco_openAudioDev(O_RDONLY))//O_RDWR
	{
		LDB_ERR_MSGF("unable to open audio device: %s!", LOCO_SOUND_DEV);
		loco_rcdFree();
		return -1;
	}

	/*创建录音工作线程*/
	f_rcdStop = 0;

	if( pthread_create(&f_rcdThrd, NULL, loco_recorder, NULL) != 0)
	{
		LDB_ERR_MSGF("can not create recorder thread");
		return -1;
	} 
	
	return 0;
}

//在开始播放的时候将录音设备关闭,然后以Writer only方式打开
void loco_rcdStop()
{
	/*停止线程*/
	f_rcdStop = 1;
	f_rcdPause = 0;

	if (f_rcdThrd != -1)
	{
		if ( pthread_join(f_rcdThrd, NULL) != 0)
		{
			LDB_DEBUG_MSGF("recorder thread exits abnormally");
		}

		f_rcdThrd = -1;
	}

	/*关闭设备*/
	loco_dspClose();

	/*关闭录音文件*/
	if (NULL != f_pCurrFile)
		fclose(f_pCurrFile);
	f_pCurrFile = NULL;
}

void loco_rcdPause()
{
	pause_Lock
		LDB_DEBUG_MSGF("Pause recording...");
		f_rcdPause++;
	pause_Unlock
}

void loco_rcdResume()
{
	//如果已经停止,启动之
	if(f_rcdStop == 1)
	{
		loco_rcdFree();
		loco_rcdInit();
	}

	if (f_rcdPause == 0)
		return;

	loco_adpcmResetEncoder();

	pause_Lock
		f_rcdPause--;
		LDB_DEBUG_MSGF("Resume recording...");
	pause_Unlock
}

int loco_rcdEnsureSoundDir(S_SndDesc *pdesc, char *pdir)
{
	char dir[SYS_PATH_MAX_LEN];
	char date[9], train[6];

	memset(date, 0, sizeof(date));
	strncpy(date, pdesc->date_start, 8);
	memset(train, 0, sizeof(train));
	snd_descGetTrain(pdesc->train_id, train, sizeof(train));
	memset(dir, 0, SYS_PATH_MAX_LEN);
	sprintf(dir, "%s/%s.%05d.%s", SYS_SOUND_SAVE_DIR, train, pdesc->driver, date);
	if ( strcmp(dir, pdir) == 0)
		return 0;

	memset(pdir, 0, SYS_PATH_MAX_LEN);
	strcpy(pdir, dir);
	if (-1 == ldb_fileCreateDir(pdir) )
		return -1;
	
	memset(dir, 0, SYS_PATH_MAX_LEN);
	sprintf(dir, "save sound file to %s", pdir);
	log_info(dir);

	return 0;
}
//最小检查个数
#define CHECK_NUM_MIN 1024

int loco_rcdGetValue(int checknum)
{
	int i, count, value;
	short sample;
	
	value = 0;
	count = checknum/2;
	for (i=0; i<count; )
	{
		sample = f_bufSample[i++] & 0xff;
		sample |= (f_bufSample[i++] << 8) & 0xff00;

		value += abs(sample);
	}

	value = value*2 / count;

	return value;
}

void *loco_recorder(void *arg)
{
	char b_pause, silence_count;
	char dir[SYS_PATH_MAX_LEN];
	char log[SYS_PATH_MAX_LEN];
	int test_counter;

	int nread, nblock, check_value;
	S_SndDesc *pdesc;
	int sample_size, sample_min_size;
	
	sample_min_size = g_voiceMinLen*SAMPLE_RATE*2;

	while(-1 != f_audiofd && f_rcdStop == 0)
	{
		loco_adpcmResetEncoder();
		
		f_pCurrFile = NULL;

		b_pause = 1;
		test_counter = 0;
		//语音检测暂停
		while(b_pause == 1)
		{
			if (f_rcdStop == 1)
			{
				goto RECORDER_END;
			}

			nread = loco_dspRead(f_bufSample, SAMPLE_BUFFER_SIZE);
			if (nread <= 0)
				goto RECORDER_END;

			if (nread < CHECK_NUM_MIN)
				continue;
				
			check_value = loco_rcdGetValue(nread);
#if 0
//#if _TEST
			if (test_counter++ == 5)
			{
				printf("Before Recording: v=%d\n", check_value);
				test_counter = 0;
			}
//#endif
#endif
			if (check_value > g_voiceMin)
			{
				memset(log, 0, sizeof(log));
				sprintf(log, "Recording start for voice %d", check_value);
				log_info(log);
				printf(log);
				printf("\n");

				b_pause = 0;
			}
		}
		sample_size = nread;

		//通知串口单元开始录音了
		loco_commReply(COMM_MSG_RESUME);

		//得到当前的列车信息
		pdesc = snd_descGet();

		//确保录音目录存在
		if (-1 == loco_rcdEnsureSoundDir(pdesc, dir) )
		{
			g_stopRequest = 1;
			goto RECORDER_END;
		}

		//确保磁盘空间足够
		if ( 0 != loco_ensureDiskSpace(dir))
		{
			g_stopRequest = 1;
			goto RECORDER_END;
		}
		
		//创建一个新文件
		memset(f_currFileName, 0, SYS_PATH_MAX_LEN);
		f_pCurrFile = ldb_fileCreate(dir, pdesc->date_start, f_currFileName);
		if (NULL == f_pCurrFile)
			continue;
		
		LDB_DEBUG_MSGF("Record a new file %s", f_currFileName);
		//在文件头写入描述信息
		fwrite(pdesc, 1, sizeof(S_SndDesc), f_pCurrFile);
		
		//压缩
		nblock = loco_adpcmEncode(f_bufSample, nread, SAMPLE_PER_BLOCK, BLOCK_ALIGN, f_bufCode, CODE_BUFFER_SIZE);
		//写入文件
		if (nblock > 0)
			ldb_fileWrite(f_pCurrFile, f_bufCode, nblock*BLOCK_ALIGN);
		
		//开始录音
		silence_count = 0;
		while(b_pause == 0 && f_rcdStop == 0)// && f_rcdPause == 0
		{
			memset(f_bufSample, 0, SAMPLE_BUFFER_SIZE);
			memset(f_bufCode, 0, CODE_BUFFER_SIZE);

			//读数据
			nread = loco_dspRead(f_bufSample, SAMPLE_BUFFER_SIZE);

			if (nread <= 0)
				goto RECORDER_END;
#if 0
//#if _TEST
			check_value = loco_rcdGetValue(nread);
			memset(log, 0, sizeof(log));
			sprintf(log, "In Recording: v=%d", check_value);
			log_info(log);
			printf(log);
			printf("\n");
//#endif
#endif
			//压缩
			nblock = loco_adpcmEncode(f_bufSample, nread, SAMPLE_PER_BLOCK, BLOCK_ALIGN, f_bufCode, CODE_BUFFER_SIZE);

			//写入文件
			if (0 != ldb_fileWrite(f_pCurrFile, f_bufCode, nblock*BLOCK_ALIGN))
				break;
			
			sample_size += nread;

			if (nread < CHECK_NUM_MIN)
				continue;

			//检查是否应该暂停
			if (sample_size > sample_min_size || silence_count > 0)
			{
				check_value = loco_rcdGetValue(CHECK_NUM_MIN);
				if (check_value <= g_voiceMin )
					silence_count++;
				else
				{
					sample_size = 0;
					silence_count = 0;
				}
				
				if (silence_count > 5)
				{
					memset(log, 0, sizeof(log));
					sprintf(log, "Recording stop for voice %d", check_value);
					log_info(log);
					printf(log);
					printf("\n");

					b_pause = 1;
				}
			}
		}

		//通知串口单元暂停录音了
		loco_commReply(COMM_MSG_PAUSE);

		fclose(f_pCurrFile);
		f_pCurrFile = NULL;

		memset(f_prevFileName, 0, SYS_PATH_MAX_LEN);
		strcpy(f_prevFileName, f_currFileName);

		LDB_DEBUG_MSGF("Close file %s", f_currFileName);
	}

RECORDER_END:
	LDB_DEBUG_MSGF("Exiting recorder thread...");

	/*清理工作*/
	loco_dspClose();

	if (NULL != f_pCurrFile)
	{
		fclose(f_pCurrFile);
		f_pCurrFile = NULL;
	}

	memset(f_prevFileName, 0, SYS_PATH_MAX_LEN);
	strcpy(f_prevFileName, f_currFileName);
	
	f_rcdStop = 1;

	f_rcdThrd = -1;

	return NULL;
}

int  loco_rcdIsStop()
{
	return f_rcdStop;
}

int loco_rcdIsForceStop()
{
	return f_forceStop;
}

void loco_rcdSetForceStop()
{
	f_forceStop = 1;
}

void loco_rcdClearForceStop()
{
	f_forceStop = 0;
}
/*************************************************/
/*************************************************/
void loco_rcdPlayFile(FILE *pfile)
{
	int nblock, ncode;
	
	loco_adpcmResetDecoder();
	
	/*打开声音设备*/
	if (-1 == loco_openAudioDev(O_WRONLY) )
	{
		log_error("failed to open audio device!");
		return;
	}

	do
	{
		memset(f_bufSample, 0, SAMPLE_BUFFER_SIZE);
		memset(f_bufCode, 0, CODE_BUFFER_SIZE);

		//读数据
		ncode = fread(f_bufCode, 1, CODE_BUFFER_SIZE, pfile);
		if (ncode == EOF || ncode <= 0)
			break;
#if ADPCM_COMPRESSION
		//解压缩
		nblock = loco_adpcmDecode(f_bufCode, ncode, BLOCK_ALIGN, f_bufSample, SAMPLE_BUFFER_SIZE, SAMPLE_PER_BLOCK);
		//写到设备
		
		if (0 > loco_dspWrite(f_bufSample, nblock*2*SAMPLE_PER_BLOCK))
			break;
#else
		if (0 > write(f_audiofd, f_bufCode, ncode))
			break;
#endif

	}while(1);

	loco_dspClose();
}

void loco_audioPrompt(const char *apid)
{
	FILE *pfile;
	char path[SYS_PATH_MAX_LEN];
	memset(path, 0, sizeof(path));
	
	if (f_forceStop == 1)
	{
		return;
	}

	loco_rcdSetForceStop();
	
	//播放的时候,暂停录音
	loco_rcdStop();
	sleep(1);
	
	if (g_sysSampleRate == 16)
	{
		sprintf(path, "%s%d", apid, g_sysSampleRate);
		if (ldb_fileExists(path) == 0)
			goto AUDIO_PROMPT_END;

	
		pfile = fopen(path, "rb");
	}
	else
	{
		if (ldb_fileExists(apid) == 0)
			goto AUDIO_PROMPT_END;

	
		pfile = fopen(apid, "rb");
	}
	
	if (pfile != NULL)
	{
		loco_rcdPlayFile(pfile);
		fclose(pfile);
		pfile = NULL;
	}

AUDIO_PROMPT_END:

	loco_rcdStart();
	
	loco_rcdClearForceStop();
}

int  loco_testerStart()
{
	FILE *pfile;
	S_SndDesc tmpDesc;
	
	if (f_forceStop == 1)
	{
		return 0;
	}

	loco_rcdSetForceStop();
	//停止录音
	loco_rcdStop();

	sleep(1);

	if (ldb_fileExists(f_prevFileName) == 0)
	{
		//继续录音
		loco_rcdStart();
		loco_rcdClearForceStop();
		return -1;
	}
	
	pfile = fopen(f_prevFileName, "rb");
	if (pfile == NULL)
	{
		//继续录音
		loco_rcdStart();
		loco_rcdClearForceStop();
		return -1;
	}
	
	fread(&tmpDesc, 1, sizeof(tmpDesc), pfile);
	loco_rcdPlayFile(pfile);
	fclose(pfile);
	pfile = NULL;
	
	sleep(1);
	loco_rcdStart();

	loco_rcdClearForceStop();
	return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -